diff options
Diffstat (limited to '')
273 files changed, 16663 insertions, 1853 deletions
diff --git a/.github/workflows/ci-deb.yml b/.github/workflows/ci-deb.yml index 965c926..131f907 100644 --- a/.github/workflows/ci-deb.yml +++ b/.github/workflows/ci-deb.yml @@ -16,11 +16,12 @@ jobs: strategy: matrix: env: - - { NAME: "ubuntu-18.04", OS: "ubuntu:bionic-20220801" } - { NAME: "ubuntu-20.04", OS: "ubuntu:20.04" } - { NAME: "ubuntu-22.04", OS: "ubuntu:22.04" } + - { NAME: "ubuntu-24.04", OS: "ubuntu:24.04" } - { NAME: "debian-10", OS: "debian:buster" } - { NAME: "debian-11", OS: "debian:bullseye" } + - { NAME: "debian-12", OS: "debian:bookworm" } - { NAME: "debian-sid", OS: "debian:sid" } fail-fast: false @@ -52,7 +53,7 @@ jobs: run: | apt-get install -y --no-install-recommends git-core ca-certificates - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: freeradius @@ -65,7 +66,7 @@ jobs: - name: Install build dependencies run: | - apt-get install -y --no-install-recommends build-essential devscripts quilt equivs procps + apt-get install -y --no-install-recommends build-essential devscripts quilt equivs procps fakeroot debian/rules debian/control mk-build-deps -irt"apt-get -y" debian/control working-directory: freeradius @@ -87,7 +88,7 @@ jobs: mv *.deb debs/ - name: Restore eapol_test build directory from cache - uses: actions/cache@v3 + uses: actions/cache@v4 id: hostapd-cache with: path: ${{ env.HOSTAPD_BUILD_DIR }} @@ -108,7 +109,7 @@ jobs: working-directory: freeradius - name: Store DEBs - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: debs-${{ matrix.env.NAME }} path: debs @@ -138,11 +139,12 @@ jobs: strategy: matrix: env: - - { NAME: "ubuntu-18.04", OS: "ubuntu:bionic-20220801" } - { NAME: "ubuntu-20.04", OS: "ubuntu:20.04" } - { NAME: "ubuntu-22.04", OS: "ubuntu:22.04" } + - { NAME: "ubuntu-24.04", OS: "ubuntu:24.04" } - { NAME: "debian-10", OS: "debian:buster" } - { NAME: "debian-11", OS: "debian:bullseye" } + - { NAME: "debian-12", OS: "debian:bookworm" } - { NAME: "debian-sid", OS: "debian:sid" } fail-fast: false @@ -156,7 +158,7 @@ jobs: steps: - name: Load DEBs - uses: actions/download-artifact@v3 + uses: actions/download-artifact@v4 with: name: debs-${{ matrix.env.NAME }} @@ -182,7 +184,7 @@ jobs: - name: Config test run: | - freeradius -XC + freeradius -XxC # # We now perform some post-install tests that depend on the availability @@ -194,7 +196,7 @@ jobs: mv eapol_test /usr/local/bin chmod +x /usr/local/bin/eapol_test - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: freeradius @@ -207,7 +209,7 @@ jobs: - name: Upload radius logs on failure if: ${{ failure() }} - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: radius-logs-${{ matrix.env.NAME }}.tgz path: | diff --git a/.github/workflows/ci-rpm.yml b/.github/workflows/ci-rpm.yml index 94517f3..00c56f9 100644 --- a/.github/workflows/ci-rpm.yml +++ b/.github/workflows/ci-rpm.yml @@ -15,10 +15,9 @@ jobs: strategy: matrix: env: - - { NAME: "centos-7", OS: "centos:7" } - - { NAME: "centos-8", OS: "centos:8" } - - { NAME: "rocky-8", OS: "rockylinux/rockylinux:8" } - - { NAME: "rocky-9", OS: "rockylinux/rockylinux:9" } + - { NAME: "centos-7", OS: "centos:7", BADNODE: true } + - { NAME: "rocky-8", OS: "rockylinux/rockylinux:8", BADNODE: false } + - { NAME: "rocky-9", OS: "rockylinux/rockylinux:9", BADNODE: false } fail-fast: false runs-on: ubuntu-latest @@ -29,26 +28,17 @@ jobs: env: HOSTAPD_BUILD_DIR: /tmp/eapol_test.ci HOSTAPD_GIT_TAG: hostapd_2_8 + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: ${{ matrix.env.BADNODE }} name: "RPM build" steps: - # - # Centos9 is EOL, so we need the below tricks to get it to work. - # - # Converting from CentOS Linux 8 to CentOS Stream 8 is the "official" process - # (see centos.org/centos-stream/#centos-stream-8): - # - - name: Some hacks for CentOS 8 (EOL) to work again. - if: ${{ matrix.env.NAME == 'centos-8' }} + - name: Fix up CentOS 7 repositories + if: ${{ matrix.env.NAME == 'centos-7' }} run: | - sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-* - sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-* - yum upgrade -y - yum -y --disablerepo '*' --enablerepo extras swap centos-linux-repos centos-stream-repos - yum clean all && yum makecache - yum distro-sync -y --allowerasing + sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* + sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* # Required so that the checkout action uses git protocol rather than the GitHub REST API. # make rpm requires the FR directory to be a git repository. @@ -57,8 +47,14 @@ jobs: run: | yum install -y https://packages.endpointdev.com/rhel/7/os/x86_64/git-core-2.30.1-1.ep7.x86_64.rpm - - name: Install distro git for Rocky and CentOS 8. - if: ${{ startsWith(matrix.env.NAME, 'rocky-') || matrix.env.NAME == 'centos-8' }} + # Rocky 9 uses dnf by default. CentOS 7 doesn't have dnf. Install yum on Rocky 9 so all distros work + - name: Install yum + if: ${{ matrix.env.NAME == 'rocky-9'}} + run: | + dnf install -y yum + + - name: Install distro git for Rocky. + if: ${{ startsWith(matrix.env.NAME, 'rocky-') }} run: | yum install -y git-core @@ -89,8 +85,8 @@ jobs: run: | yum install -y epel-release - - name: Enable PowerTools on Rocky 8 and CentOS 8. - if: ${{ matrix.env.NAME == 'rocky-8' || matrix.env.NAME == 'centos-8' }} + - name: Enable PowerTools on Rocky 8. + if: ${{ matrix.env.NAME == 'rocky-8' }} run: | yum install -y yum-utils yum config-manager --enable PowerTools || : @@ -142,8 +138,8 @@ jobs: openssl version # For pkill and ps - - name: Enable procps-ng on Centos and Rocky - if: ${{ startsWith(matrix.env.NAME, 'centos-8') || startsWith(matrix.env.NAME, 'rocky-') }} + - name: Enable procps-ng on Rocky + if: ${{ startsWith(matrix.env.NAME, 'rocky-') }} run: | yum install -y procps-ng @@ -205,10 +201,9 @@ jobs: strategy: matrix: env: - - { NAME: "centos-7", OS: "centos:7" } - - { NAME: "centos-8", OS: "centos:8" } - - { NAME: "rocky-8", OS: "rockylinux/rockylinux:8" } - - { NAME: "rocky-9", OS: "rockylinux/rockylinux:9" } + - { NAME: "centos-7", OS: "centos:7", BADNODE: true } + - { NAME: "rocky-8", OS: "rockylinux/rockylinux:8", BADNODE: false } + - { NAME: "rocky-9", OS: "rockylinux/rockylinux:9", BADNODE: false } fail-fast: false runs-on: ubuntu-latest @@ -216,25 +211,23 @@ jobs: container: image: ${{ matrix.env.OS }} + env: + ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: ${{ matrix.env.BADNODE }} + name: "RPM install test" steps: - # - # Centos9 is EOL, so we need the below tricks to get it to work. - # - # Converting from CentOS Linux 8 to CentOS Stream 8 is the "official" process - # (see centos.org/centos-stream/#centos-stream-8): - # - - name: Some hacks for CentOS 8 (EOL) to work again. - if: ${{ matrix.env.NAME == 'centos-8' }} + - name: Fix up CentOS 7 repositories + if: ${{ matrix.env.NAME == 'centos-7' }} run: | - sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/CentOS-Linux-* - sed -i 's|#baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g' /etc/yum.repos.d/CentOS-Linux-* - yum upgrade -y - yum -y --disablerepo '*' --enablerepo extras swap centos-linux-repos centos-stream-repos - yum clean all && yum makecache - yum distro-sync -y --allowerasing + sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* + sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* + + - name: Install yum + if: ${{ matrix.env.NAME == 'rocky-9'}} + run: | + dnf install -y yum - name: LTB repo for CentOS and Rocky 8 if: ${{ startsWith(matrix.env.NAME, 'centos-') || matrix.env.NAME == 'rocky-8' }} @@ -252,8 +245,8 @@ jobs: run: | yum install -y epel-release - - name: Enable PowerTools on Centos 8 and Rocky 8 - if: ${{ matrix.env.NAME == 'centos-8' || matrix.env.NAME == 'rocky-8' }} + - name: Enable PowerTools on Rocky 8 + if: ${{ matrix.env.NAME == 'rocky-8' }} run: | yum install -y yum-utils yum config-manager --enable PowerTools || : @@ -280,9 +273,16 @@ jobs: run: | yum install -y *.rpm + - name: Ensure certificates are created + if: ${{ matrix.env.NAME == 'centos-7' }} + run: | + if [ ! -e /etc/raddb/certs/server.pem ]; then + /sbin/runuser -g radiusd -c 'umask 007; /etc/raddb/certs/bootstrap' + fi + - name: Config check run: | - radiusd -XC + radiusd -XxC # # We now perform some post-install tests that depend on the availability diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de5e7ed..caf9753 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -89,7 +89,7 @@ jobs: MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: yes ports: - 3306:3306 - options: --health-cmd="mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 10 + options: --health-cmd="mariadb-admin ping" --health-interval 10s --health-timeout 5s --health-retries 10 postgres: image: ${{ needs.pre-ci.outputs.docker_prefix }}postgres @@ -201,7 +201,7 @@ jobs: - name: 'Restore OpenSSL 3.0 from the cache' if: ${{ matrix.env.LIBS_ALT == 'yes' }} - uses: actions/cache@v3 + uses: actions/cache@v4 id: openssl-cache with: path: /opt/openssl/ @@ -233,7 +233,7 @@ jobs: [ -d /opt/openssl ] && export PATH=/opt/openssl/bin:$PATH printf "\nopenssl: " ; openssl version - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Build eapol_test run: | @@ -290,7 +290,7 @@ jobs: if: ${{ matrix.env.CC == 'clang' }} - name: "Clang Static Analyzer: Store assets on failure" - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: clang-scan.tgz path: build/plist/**/*.html @@ -460,14 +460,14 @@ jobs: runs-on: ubuntu-latest name: "Merge into upstream" steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: fetch-depth: 0 lfs: false persist-credentials: false - # Note: This also opportunistically updates the developer's branch with commits from + # Note: This also opportunistically updates the developer's branch with commits from # the main repository. - # This update may fail if the developer has pushed additional commits since the + # This update may fail if the developer has pushed additional commits since the # workflow started. This is normal, and we ignore the failure. - name: "Merge into upstream dev branch and update local branch" run: | diff --git a/.github/workflows/crossbuild.yml b/.github/workflows/crossbuild.yml new file mode 100644 index 0000000..d3d9186 --- /dev/null +++ b/.github/workflows/crossbuild.yml @@ -0,0 +1,91 @@ +name: Check Crossbuild + +on: + push: + branches-ignore: + - coverity_scan + workflow_dispatch: + +env: + CI: 1 + GH_ACTIONS: 1 + DEBIAN_FRONTEND: noninteractive + APT_OPTS: "-y --no-install-recommends" + +jobs: + # + # Generate matrix based on crossbuild docker directories. + # + gen-matrix: + + runs-on: ubuntu-latest + + outputs: + matrix: ${{ steps.gen-matrix.outputs.matrix }} + + steps: + + - uses: actions/checkout@v4 + with: + lfs: false + + - id: gen-matrix + name: Generate matrix based on crossbuild targets + run: | + cd scripts/crossbuild/docker + M=$(ls | perl -n -e '{chomp; push @L,"\"$_\""} END{print "{\"env\":[{\"OS\":",join("},{\"OS\":",@L),"}]}"}') + echo "Matrix: $M" + echo matrix=$M >> $GITHUB_OUTPUT + + # + # Run crossbuild target for each given OS. This will build the + # Docker image and run `make test` inside. + # + crossbuild: + needs: + - gen-matrix + + runs-on: ubuntu-20.04 + + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.gen-matrix.outputs.matrix) }} + + env: ${{ matrix.env }} + + name: "v3.2.x-${{ matrix.env.OS }}" + + steps: + + - uses: actions/checkout@v4 + with: + fetch-depth: 0${{ ( matrix.env.OS != 'centos7' ) && '1' }} + + - name: Run crossbuild tests + run: | + make crossbuild.$OS + + - name: Show build log + if: ${{ success() || failure() }} + run: | + cat scripts/crossbuild/build/build.$OS + + - name: Show configure log + if: ${{ success() || failure() }} + run: | + cat scripts/crossbuild/build/configure.$OS + + - name: Show test log + if: ${{ success() || failure() }} + run: | + cat scripts/crossbuild/build/log.$OS + + # + # If the CI has failed and the branch is ci-debug then start a tmate + # session. SSH rendezvous point is emited continuously in the job output. + # + - name: "Debug: Start tmate" + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true + if: ${{ github.ref == 'refs/heads/ci-debug' && failure() }} diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..67af2a2 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,81 @@ +name: Build Dockerfiles + +on: + push: + branches-ignore: + - coverity_scan + workflow_dispatch: + +env: + CI: 1 + GH_ACTIONS: 1 + DEBIAN_FRONTEND: noninteractive + APT_OPTS: "-y --no-install-recommends" + +jobs: + # + # Generate matrix based on docker directories. + # + gen-matrix: + + runs-on: ubuntu-latest + + outputs: + matrix: ${{ steps.gen-matrix.outputs.matrix }} + + steps: + + - uses: actions/checkout@v4 + with: + lfs: false + + - id: gen-matrix + name: Generate matrix based on docker targets + run: | + cd scripts/docker/dists + M=$(ls | perl -n -e '{chomp; push @L,"\"$_\""} END{print "{\"env\":[{\"OS\":",join("},{\"OS\":",@L),"}]}"}') + echo "Matrix: $M" + echo matrix=$M >> $GITHUB_OUTPUT + + # + # Run docker target for each given OS. This will build the + # Docker image. + # + docker: + needs: + - gen-matrix + + runs-on: ubuntu-20.04 + + strategy: + fail-fast: false + matrix: ${{ fromJson(needs.gen-matrix.outputs.matrix) }} + + env: ${{ matrix.env }} + + name: "v3.2.x-${{ matrix.env.OS }}" + + steps: + + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Regenerate Dockerfile + run: | + rm scripts/docker/dists/$OS/Dockerfile || true + make docker.$OS.regen + + - name: Build docker image + run: | + make docker.$OS.build + + # + # If the CI has failed and the branch is ci-debug then start a tmate + # session. SSH rendezvous point is emited continuously in the job output. + # + - name: "Debug: Start tmate" + uses: mxschmitt/action-tmate@v3 + with: + limit-access-to-actor: true + if: ${{ github.ref == 'refs/heads/ci-debug' && failure() }} diff --git a/.github/workflows/dockerhub.yml b/.github/workflows/dockerhub.yml new file mode 100644 index 0000000..738c9c0 --- /dev/null +++ b/.github/workflows/dockerhub.yml @@ -0,0 +1,111 @@ +name: Dockerhub Dev + +on: + push: + branches-ignore: + - coverity_scan + workflow_dispatch: + +env: + CI: 1 + GH_ACTIONS: 1 + DEBIAN_FRONTEND: noninteractive + APT_OPTS: "-y --no-install-recommends" + DOCKER_REPOSITORY: freeradius-dev + BRANCH: v3.2.x + +jobs: + # + # Run docker target for each given OS and ARCH. This will + # build the Docker images. + # + build_docker_images: + runs-on: ubuntu-22.04 + if: github.repository_owner == 'FreeRADIUS' + + strategy: + matrix: + env: + - { OS: "ubuntu", ARCH: "linux/amd64", NAME: "amd64" } + - { OS: "ubuntu", ARCH: "linux/arm/v7", NAME: "armv7" } + - { OS: "ubuntu", ARCH: "linux/arm64/v8", NAME: "arm64v8" } + - { OS: "alpine", ARCH: "linux/amd64", NAME: "amd64" } + - { OS: "alpine", ARCH: "linux/arm/v6", NAME: "armv6" } + - { OS: "alpine", ARCH: "linux/arm/v7", NAME: "armv7" } + - { OS: "alpine", ARCH: "linux/arm64/v8", NAME: "arm64v8" } + + fail-fast: false + + name: "${{ matrix.env.OS }}-${{ matrix.env.ARCH }}" + + steps: + + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Regenerate Dockerfiles + run: | + rm scripts/docker/dists/*/Dockerfile || true + make docker.regen + + - name: Install qemu + run: | + sudo apt-get update + sudo apt-get install ${APT_OPTS} qemu-user-static + + - name: Build docker image + run: | + make docker-${{ matrix.env.OS }} \ + DOCKER_TAG="$DOCKER_REPOSITORY" \ + DOCKER_BUILD_ARGS="--no-cache --platform ${{ matrix.env.ARCH }}" \ + DOCKER_VERSION="${{ matrix.env.NAME }}-$BRANCH" + + - name: Docker login + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Push arch image to Docker registry + shell: bash + run: | + make docker-push-${{ matrix.env.OS }} \ + DOCKER_TAG="$DOCKER_REPOSITORY" \ + DOCKER_VERSION="${{ matrix.env.NAME }}-$BRANCH" + + + manifest: + runs-on: ubuntu-22.04 + needs: build_docker_images + if: github.repository_owner == 'FreeRADIUS' + + strategy: + matrix: + env: + - { OS: "ubuntu", ARCHS: "amd64 armv7 arm64v8", SUFFIX: "" } + - { OS: "alpine", ARCHS: "amd64 armv6 armv7 arm64v8", SUFFIX: "-alpine" } + + fail-fast: false + + name: "manifest-${{ matrix.env.OS }}" + + steps: + + - uses: actions/checkout@v4 + with: + fetch-depth: 1 + + - name: Docker login + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_PASSWORD }} + + - name: Create manifests + shell: bash + run: | + make docker-ci-manifest \ + DOCKER_TAG="$DOCKER_REPOSITORY" \ + DOCKER_VERSION="$BRANCH${{ matrix.env.SUFFIX }}" \ + DOCKER_ARCHS="${{ matrix.env.ARCHS }}" diff --git a/.vscode/c_cpp_properties.json b/.vscode/c_cpp_properties.json index 4ae342a..02a16ac 100644 --- a/.vscode/c_cpp_properties.json +++ b/.vscode/c_cpp_properties.json @@ -6,6 +6,11 @@ "/usr/include/**", "${workspaceFolder}/src/**" ], + "forcedInclude": [ + "${workspaceFolder}/src/freeradius-devel/autoconf.h", + "${workspaceFolder}/src/freeradius-devel/build.h", + "${workspaceFolder}/src/freeradius-devel/features.h" + ], "defines": [], "compilerPath": "/usr/bin/clang", "cStandard": "c11", @@ -17,6 +22,11 @@ "includePath": [ "${workspaceFolder}/src/**" ], + "forcedInclude": [ + "${workspaceFolder}/src/freeradius-devel/autoconf.h", + "${workspaceFolder}/src/freeradius-devel/build.h", + "${workspaceFolder}/src/freeradius-devel/features.h" + ], "defines": [], "compilerPath": "/usr/bin/clang", "cStandard": "c11", diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..fb1d4c1 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "files.trimTrailingWhitespace": true, + "files.insertFinalNewline": true, + "[diff]" : { + "files.trimTrailingWhitespace": false + } +} @@ -381,7 +381,7 @@ rpm: rpmbuild/SOURCES/freeradius-server-$(RADIUSD_VERSION_STRING).tar.bz2 echo "ERROR: Required depdendencies not found, install them with: yum-builddep redhat/freeradius.spec"; \ exit 1; \ fi - ${Q}QA_RPATHS=0x0003 rpmbuild --define "_topdir `pwd`/rpmbuild" -bb redhat/freeradius.spec + ${Q}QA_RPATHS=0x0003 rpmbuild --define "_topdir `pwd`/rpmbuild" -bb $(RPMBUILD_FLAGS) redhat/freeradius.spec # # Developer checks @@ -1 +1 @@ -3.2.3 +3.2.5 diff --git a/doc/ChangeLog b/doc/ChangeLog index 6b7006e..0392c28 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,102 @@ +FreeRADIUS 3.2.5 Tue 09 Jul 2024 12:00:00 UTC urgency=high + Configuration changes + * BlastRADIUS mitigations have been added to the "security" + section. See "require_message_authenticator" and also + "limit_proxy_state". + * BlastRADIUS mitigations have been added to radclient. + See "man radclient", and the "-b" option. + + Feature improvements + * TOTP now supports TOTP-Time-Offset for tokens with times that + are out of sync. See mods-available/totp + * radclient now supports forcing the Request Authenticator and ID + for Access-Request packets. + * Update dictionary.3gpp. + * Update advice on shared secrets, including suggesting a secure + method for generating useful secrets. + + Bug fixes + * Allow proxying by pool / home server name to work with auth+acct servers + * Fix OpenSSL API usage which sometimes caused crash in MS-CHAP + Previously it would either always crash immediately, or never crash. + * Fix packet statistics. Stop double counting some packets, + and track packet statistics even if a socket is closed. + * Reverted patch in TTLS which broke compatibility with some systems. + * Don't crash in debug mode when multiple intermediate certs are used + Patch from Alexander Chernikov. + +FreeRADIUS 3.2.4 Wed 29 May 2024 12:00:00 EDT urgency=low + Configuration changes + * Better handle backslashes in strings in the configuration files. + If the configuration items contain backslashes, then behavior may change. + However, the previous behavior didn't work as expected, and therefore is not + likely to be used. + * reject_delay no longer applies to proxied packets. All servers should now + set "reject_delay = 1" for security and scalability. + * %{randstr:...} now returns the requested amount of data, instead of + one too many bytes. + + Feature improvements + * Preliminary support for TEAP. + * Update EAP module pre_proxy checks to make them less restrictive. + This prevents the "middle box" effect from affecting future traffic. + * Many fixes and updates for Docker images + * Add dpsk module. See mods-available/dpsk + * Print out what cause the TLS operations to be made, such as the EAP + method name (peap, ttls, etc), or RADIUS/TLS listen / proxy socket. + * Add auto_escape to sample SQL module config + * Add 'if not exists' to mysql create table queries. ref #5032 (#5137) + * Update dictionary.aruba; add dictionary.tplink, dictionary.alphion + * Allow for 'encrypt=1' attributes to be longer than 128 characters. + * Added "radsecret" program which generates strong secrets. See the + top of the "clients.conf" file for more information. + * radclient now prints packets as hex when using -xxx. + * Added "-t timeout" to radsniff. It will stop processing packets + after <timeout> seconds. + * Support "interface = ..." on OSX and other *BSD which have IP_BOUND_IF. + * The detail module now has a "dates_as_integer" configuration item. + See mods-available/detail for more information. + * Add lookback/lookforward steps and more configuration to totp. See + mods-available/totp. + * Add "time_since" xlat to calculate elapsed time in seconds, milliseconds + and microseconds. + * Support "Post-Auth-Type Challenge" in the inner tunnel. Patch from + Alexander Clouter. PR #5320. + * Add "proxy_dedup_window". See radiusd.conf. + * Document KRB5_CLIENT_KTNAME in the "env" section of radiusd.conf. + * Add "dedup_key" for misbehaving supplicants. See mods-available/eap + + Bug fixes + * Fix corner case with empty defaults in rlm_files. Fixes #5035 + * When we have multiple attributes of the same name, always use the + canonical attribute + * Make FreeRADIUS-Server-EMA* attributes work again for home server + exponential moving average statistics. + * Don't send the global server stats when asked for client stats. They + use the same attributes, so the result is confusing. + * Fix multiple typos in MongoDB query.conf (#5130) + * Add define for illumos. Fixes #5135 + * Add client configuration for TLS PSK. + * Permit originate CoA after proxying to an internal virtual server + * Use virtual server "default" when passed "-i" and "-p" on the command line. + * Fix locking issues with rlm_python3. + * The detail file reader will catch bad times in the file, and will not + update Acct-Delay-Time with extreme values. + * Fix issue where Message-Authenticator was calculated incorrectly for + CoA / Disconnect ACK and NAK packets. + * Update Python thread and error handling. Fixes #5208. + * Fix handling of Session-State when proxying. Fixes #5288. + * Run relevant post-proxy Fail-* section on CoA / Disconnect timeout. + * Add "limit" section to AWS health check configurtion. Fixes 35300. + * Use MAX in sqlite queries instead of GREATEST. + * Fix typo in Mongo queries. Fixes #5301. + * Fix occasional crash with bad home servers. Fixes #5308. + * Minor bug fixes to the SQL freetds modules. + * Fix blocking issue with RADIUS/TLS connection checks. + * Fix run-time crash on configuration typos of %{substr ...} instead + of %{substr:...} Fixes #5321. + * Fix crash with TLS Status-Server requests. Fixes #5326. + FreeRADIUS 3.2.3 Fri 26 May 2023 12:00:00 EDT urgency=low Configuration changes * The rlm_ldap and rlm_sql modules now have a "max_retries" configuration diff --git a/doc/antora/antora.yml b/doc/antora/antora.yml index e345e9c..263945a 100644 --- a/doc/antora/antora.yml +++ b/doc/antora/antora.yml @@ -5,7 +5,7 @@ # name: freeradius-server title: The FreeRADIUS Server -version: '3.2.3' +version: '3.2.5' start_page: ROOT:index.adoc nav: - modules/ROOT/nav.adoc diff --git a/doc/antora/modules/concepts/nav.adoc b/doc/antora/modules/concepts/nav.adoc new file mode 100644 index 0000000..493b956 --- /dev/null +++ b/doc/antora/modules/concepts/nav.adoc @@ -0,0 +1,6 @@ +* xref:index.adoc[Concepts] +** General +*** xref:aaa.adoc[AAA] +** Modules +*** LDAP +**** xref:modules/ldap/authentication.adoc[Authentication] diff --git a/doc/antora/modules/concepts/pages/aaa.adoc b/doc/antora/modules/concepts/pages/aaa.adoc new file mode 100644 index 0000000..294305c --- /dev/null +++ b/doc/antora/modules/concepts/pages/aaa.adoc @@ -0,0 +1,60 @@ += AAA + +== Authorization, Authentication, and Accounting request handling + +There are a lot of questions about misconfigured FreeRADIUS servers +because of misunderstanding of FreeRADIUS operations. This document +explains how the server operates. + +Normally there are two steps in processing an authentication request +coming from a NAS in FreeRADIUS: authorization and authentication. +If we use FreeRADIUS as a proxy to re-send the request to another +RADIUS server there will be additional steps. + +=== Authorization + +Authorization is the process of finding and returning information +about what the user is allowed to do. For example, finding out what +kind of authentication methods they are allowed to run, and what VLAN +the user should be placed into. + +Authorization modules generally "get data" from somewhere, +e.g. `ldap`, `sql`, `files`, etc. + +The authentication method is usually determined when the server gets +the users credentials from a database. Once the credentials are +available, the server can authenticate the user. + +=== Authentication + +Authentication is simply a process of comparing user’s credentials in +request with the "known good" credentials retrieved from a +database. Authentication usually deals with password +encryption. The modules `pap`, `chap`, `mschap`, etc. do authentication. + +Some modules do both authentication and limited authorization. For +example, the `mschap` module authenticates MS-CHAP credentials, but it +may also be used as an authorization module, which verifies that +request contains `MS-CHAP` related attribute. If so, the module +instructs the server to use `mschap` for authentication, too + +These dual modules are usually related to protocol-specific +attributes, such as the `pap` module for the `User-Password` +attribute, `chap` for `CHAP-Password`, `mschap` for `MS-CHAP-*`, etc. + +=== Request Processing + +When the server processes requests, it manages four +xref:unlang:list.adoc[attribute lists]: + +`request`:: attributes taken from the received packet + +`reply`:: attributes which will be sent in the reply + +`control`:: attributes used to control how the server operates. These are never sent in a packet + +`session-state`:: attributes which are saved and restroed across multiple request / reply exchanges. + +All of these lists are available to all modules. All of these +lists are available in xref:unlang:index.adoc[Unlang]. + diff --git a/doc/antora/modules/concepts/pages/index.adoc b/doc/antora/modules/concepts/pages/index.adoc new file mode 100644 index 0000000..f2bc25f --- /dev/null +++ b/doc/antora/modules/concepts/pages/index.adoc @@ -0,0 +1,8 @@ += Concepts + +This section documents concerning the protocols and modules used by the +FreeRADIUS server. + +It intended to provide more theoretical information about particular subjects +than would be appropriate to include inline in module configurations or as +sidebars in howto guides. diff --git a/doc/antora/modules/concepts/pages/modules/ldap/authentication.adoc b/doc/antora/modules/concepts/pages/modules/ldap/authentication.adoc new file mode 100644 index 0000000..edc553e --- /dev/null +++ b/doc/antora/modules/concepts/pages/modules/ldap/authentication.adoc @@ -0,0 +1,203 @@ +== Authenticating Users with LDAP + +Please be aware the FreeRADIUS is an AAA server, and LDAP +is a _database_. This separation of roles means that FreeRADIUS +supports multiple kinds of authentication protocols such as `PAP`, +`CHAP`, `MS-CHAP`, etc. An LDAP database supports only one +authentication method: "bind as user". This authentication method is +compatible only with PAP. + +Our recommendation is to use LDAP as a database. FreeRADIUS should +read the "known good" password from LDAP, and then use that +information to authenticate the user. It is almost always wrong to +use the LDAP "bind as user" method for authenticating users. + +The only caveat to the above recommendation is Active Directory. For +"security" reasons, Active Directory will not return the "known good" +password to FreeRADIUS over a standard LDAP query. Therefore when +Active Directory is used, the choices are: + +PAP:: +Use "bind as user" + +MS-CHAP:: +Use xref:raddb:mods-available/ntlm_auth.adoc[`ntlm`] or xref:raddb:mods-available/winbind.adoc[`winbind`]. + +Due to the limitations of Active Directory, There are unfortunately no +other possible choices. + +== LDAP Security Recommendations + +The credentials (username *and* password) for FreeRADIUS to use to +connect to your LDAP server(s) should be secure. We make the +following recommendations for LDAP "best practices" security. + +* Create a dedicated account for use by FreeRADIUS + +* Ensure that this account does not have administrator access + +* Ensure that this account is read-only, and has no write permissions + +* Start by using 'simple authentication' instead of + https://en.wikipedia.org/wiki/Simple_Authentication_and_Security_Layer[SASL]. + The SASL protocol should be attempted only after 'simple + authentication' has been verified to work. + +* Use TLS for connecting between FreeRADIUS and the LDAP server. See + the `tls` sub-section of the default `ldap` module for instructions + +* When storing RADIUS user profiles (quotas, `Simultaneous-Use` flags, + access time restrictions, etc) in LDAP, the LDAP schema + `doc/schemas/ldap/openldap/freeradius-radius.schema` must first be imported + into the LDAP server. + +== Authentication method compatibility + +The LDAP module is compatible a few different kinds of authentication +methods. Note that we say _compatible_, and not _supports_. LDAP +servers are databases, and do not support authentication protocols +such as CHAP, MS-CHAP, or EAP. + +PAP:: +The user supplies a `User-Password` (plaintext or EAP-TTLS/PAP) ++ +FreeRADIUS reads the "known good" password from LDAP, and compares +that to what the user entered. + +Bind as user:: +The user supplies a `User-Password` (plaintext or EAP-TTLS/PAP) ++ +FreeRADIUS uses that password to "bind as the user" to LDAP, using the +supplied `User-Name` and `User-Password. If the bind is successfull, +the user is authenticated. Otherwise, authentication fails. + +CHAP:: +The user supplies a `CHAP` password attribute. ++ +FreeRADIUS reads the "known good" password from LDAP in cleartext, and +compares that to what the user entered. + +MS-CHAP:: +The user supplies a `MS-CHAP` password attribute. Either as +MS-CHAPv2, or as PEAP/MSCHAPv2, or as EAP-TTLS/MS-CHAPv2. ++ +FreeRADIUS reads the "known good" password from LDAP in cleartext, or +as an NT hash, and compares that to what the user entered. + +All of above authentication methods other than "bind as user" require +that FreeRADIUS obtain the `userPassword` field from LDAP. If that +field is not returned to FreeRADIUS, then normal authentication is +impossible. Either FreeRADIUS has to be configured to use "bind as +user" for authentication, or the LDAP database has to be updated to +return the `userPassword` field to FreeRADIUS. This change usually +involves giving the FreeRADIUS "read-only" user permission to read the +`userPassword` field. + +Again, the best method is to test authentication is with the +xref:howto:modules/ldap/ldapsearch/index.adoc[ldapsearch] tool. +These tests *must* be run prior to configuring FreeRADIUS. We strongly +recommend having the LDAP database return the `userPassword` field to +FreeRADIUS, so that FreeRADIUS can authenticate the user. + +We also strongly recommend that the passwords be stored in LDAP as +cleartext. Otherwise, the only authentication methods that will work +are PAP and EAP-TTLS/PAP. The next section explains these issues in +more detail. + +== Password Storage Methods + +If the `userPassword` field is returned from LDAP to FreeRADIUS, that +information can be stored in a number of different formats: + +* the value can be cleartext +* the value can be prepended with a string enclosed by braces, such as with `{crypt}` or `{ssha3}`. +* the value can be have a suffix of `::`, in which case the password is generally a https://en.wikipedia.org/wiki/Base64[base64] encoded version of the real password + +TIP: Base64 values can be decoded via the command: `printf "%s" +"VALUE" | base64 -d` + +FreeRADIUS is capable of understanding and parsing all of the above +formats. There is sufficient information in the password values to +determine what format it is in (base64, binary, or text), and what +password "encryption" mechanism has been used (crypt, MD5, SHA, SSHA2, +SHA3, etc). All that is necessary is that the +xref:raddb:mods-available/ldap.adoc[ldap module] be configured to map +the `userPassword` LDAP field to the `&control.Password.With-Header` +attribute in FreeRADIUS. FreeRADIUS will then "do the right thing" to +authenticate the user. + +This mapping is done in the default module configuration. There are +no additional changes required for FreeRADIUS to correctly read and +decode the `userPassword` field from LDAP. Please see the +xref:raddb:mods-available/pap.adoc[pap module] for a full list of +supported password "encryption" formats. + +== Additional Considerations + +There are some major caveats with the above authentication methods. +The first is that we *strongly recommend* against using "bind as +user". This process is slow, and causes unnecessary churn in the +connections used to contact the LDAP server. Further, the "bind as +user" process works _only_ when a `User-Password attribute exists in +the request. If any other authentication type is used in the request, +then the "bind as user" _will not work_. There is no amount of +"fixing" things or configuration changes which will make it work. + +The second caveat is that the `CHAP` authentication type works _only_ +when a "clear text" password is stored in the LDAP database. The +`CHAP` calclulations are designed around having access to the "clear +text" password. It is impossible to use any "encrypted" or "hashed" +passwords with `CHAP`. + +The third caveat is that the `MS-CHAP` authentication type works +_only_ when a "clear text" password or "NT hashed" passwords which are +stored in the LDAP database. The `MS-CHAP` calculations are designed +around having access to "known good" passwords in those formats. It +is impossible to use any other kind of "encrypted" or "hashed" +passwords with `MS-CHAP`. + +The final caveat is that when the LDAP database contains "encrypted" +or "hashed" passwords, the _only_ authentication type which is +compatible with those passwords is PAP. i.e. when the `User-Password` +is supplied to FreeRADIUS. + +For recommendations on password storage methods in LDAP, please see +the LDAP +https://openldap.org/doc/admin24/security.html#Password%20Storage[password +storage] page. Please note that the recommendations there are made +for LDAP security, and pay no attention to the caveats described +above. When both RADIUS and LDAP are used together, then the +requirements of _both_ systems must be met in order for them to work +together. In many cases, a naive approach to LDAP security will +prevent RADIUS from working. + +The issue of a database storing passwords in clear-text has to be +balanced against the users sending clear-text passwords in +authentication protocols. While those passwords are protected by TLS +(EAP-TTLS) or by RADIUS (in it's own "encryption" mechanism), it is +generally better to use a stronger authentication method than just +PAP. + +In the end, there is no perfect solution to security requirements. +The choice may be either to give up on using a particular +authentication method, or to relax the security requirements on LDAP +and on password storage. The final decision as to which choice is +best can only be made by a local administrator. + +== Integrating Novell eDirectory with FreeRADIUS + +You can integrate Novell eDirectoryTM 8.7.1 or later with FreeRADIUS +1.0.2 onwards to allow wireless authentication for eDirectory users. By +integrating eDirectory with FreeRADIUS, you can do the following: + +* Use universal password for RADIUS authentication. Universal password +provides single login and authentication for eDirectory users. +Therefore, the users need not have a separate password for RADIUS and +eDirectory authentication. +* Enforce eDirectory account policies for users. The existing eDirectory +policies on the user accounts can still be applied even after +integrating with RADIUS. Also, you can make use of the intruder lockout +facility of eDirectory by logging the failed logins into eDirectory. + +For configuration information please refer to the Novell documentation +https://www.netiq.com/documentation/edir_radius/ diff --git a/doc/antora/modules/developers/nav.adoc b/doc/antora/modules/developers/nav.adoc new file mode 100644 index 0000000..ed6962a --- /dev/null +++ b/doc/antora/modules/developers/nav.adoc @@ -0,0 +1,7 @@ +* xref:index.adoc[Developers] +** xref:bugs.adoc[Bugs] +** xref:coding-methods.adoc[Coding Methods] +** xref:coverage.adoc[Code Coverage] +** xref:profile.adoc[Profiling] +** xref:contributing.adoc[Contributing] +** xref:release-method.adoc[Release Method] diff --git a/doc/antora/modules/developers/pages/bugs.adoc b/doc/antora/modules/developers/pages/bugs.adoc new file mode 100644 index 0000000..b63329c --- /dev/null +++ b/doc/antora/modules/developers/pages/bugs.adoc @@ -0,0 +1,194 @@ += Bugs + +== Introduction + +The FreeRADIUS web site is at https://freeradius.org/, and most +information referenced in this document can be found there. + +This document is primarily for non-developers of the FreeRADIUS +server. If you are able to patch the code to work correctly, then +we invite you to join the development list to discuss it. If +you’re the type who know little about how to code, then this is +the place for you! + +== You found a bug + +Where the server terminates ungracefully due to a bus error, +segmentation violation, or other memory error, you should create a new +issue in the issue tracker https://bugs.freeradius.org/, including +information from the debugging sections below. + +For any other issues, you should first discuss them on the +https://freeradius.org/support/[FreeRADIUS users list], to +see if anyone can reproduce them. Often there’s a simple explanation of +why the server behaves as it does, and it’s not necessarily a bug in the +code, so browse the lists’ archives of the last two months, and if you +don’t see messages about it, ask! + +If the behavior is correct but confusing, we think that’s a bug too, and +you should file a bug against our documentation. + +For more information about the users list, the lists’ archives and the +faq, please visit https://www.freeradius.org/list/users.html Please make +sure to READ and RESPECT the house-rules. You will get much better +response and much faster if you do! + +== Core dumps + +If the server (or one of the accompanying programs) core dumps, then you +should rebuild the server as follows: + +``` +$ ./configure --enable-developer +$ make +$ make install +``` + +and then run the program again. You may have to to enable core dumps, +via: + +``` +$ ulimit -c unlimited +``` + +When it core dumps, do: + +``` +$ gdb /path/to/executable /path/to/core/file +``` + +Enable logging in `gdb` via the following commands: + +``` +(gdb) set logging file gdb-radiusd.log +(gdb) set logging on +``` + +and follow the instructions in the proceeding section. + +You can also enable the `panic_action` given in +`raddb/radiusd.conf`. See the comments in that file for more details +about automatically collecting gdb debugging information when the server +crashes. + +== Debugging a live server + +If you can’t get a core dump, or the problem doesn’t result in a core +dump, you may have to run the server under gdb. To do this, ensure that +you have symbols in the binaries (i.e. a 'non-stripped' binary) by +re-building the server as described in the previous section. Then, run +the server under gdb as follows: + +``` +$ gdb radiusd +``` + +Enable logging in `gdb` via the following commands: + +``` +(gdb) set logging file gdb-radiusd.log +(gdb) set logging on +``` + +Tell `gdb` to pass any necessary command-line arguments to the server: + +``` +(gdb) set args ... +``` + +Where the ``…'' are the command-line arguments you normally pass to +radiusd. For debugging, you probably want to do: + +``` +(gdb) set args -fxx +``` + +Then, do: + +``` +(gdb) run +``` + +When something interesting happens, you can hit CTRL-C in the window, +and you should be back at the gdb prompt: + +``` +(gdb) +``` + +And follow the instructions in the next section. + +== Obtaining useful information + +Once you have a core dump loaded into gdb, or FreeRADIUS running under +gdb, you may use the commands below to get useful information about the +state of the server. + +If the server was built with threads, you can do: + +``` +(gdb) info threads +``` + +Which will give you information about the threads. If the server isn’t +threaded, that command-line will print a message saying so. + +Then, do: + +``` +(gdb) thread apply all bt full +``` + +If the server isn’t threaded, the `thread apply` section isn’t +necessary + +The output should be printed to the screen, and also sent to the +`gdb-radiusd.log` file. + +You should then submit the information from the log file, along with any +server output, the output of `radiusd -xv`, and information about your +operating system to: + +https://bugs.freeradius.org/ + +Submitting it to the bug database ensures that the bug report won’t get +forgotten, and that it will be dealt with in due course. + +You should provide the issue number in any mail sent to the user’s list. + +== Valgrind + +On Linux systems, `valgrind` is a useful tool that can catch certain +classes of bugs. To use it, run the server via: + +``` +$ valgrind --tool=memcheck --leak-check=full radiusd -Xm +``` + +It will print out certain kinds of errors to the screen. There may be a +number of errors related to OpenSSL, dlopen(), or libtldl. We cannot do +anything about those problems. However, any errors that are inside of +the FreeRADIUS source should be brought to our attention. + +== Running with `screen` + +If the bug is a crash of the server, and it takes a long time for the +crash to happen, perform the following steps: + +* Log in as root. +* Open a https://www.gnu.org/software/screen/[screen] session: `$ screen bash`. +* Make sure FreeRADIUS is not running. +* Make sure you have all the debug symbols about, or a debugable version +of the server installed (one built with `--enable-developer` as above). +* Configure screen to log to a file by pressing `Ctrl+a`, then `h`. +* Type `gdb /path/to/radiusd` (or /path/to/freeradius on Debian). +* At the `(gdb)` prompt, type `run -X`. +* Detach from screen with `Ctrl+a`, `d`. +* When you notice FreeRADIUS has died, reconnect to your screen session +`$ screen -D -r`. +* At the `(gdb)` prompt type `where` or for _lots_ of info try +`thread apply all bt full`. +* Tell screen to stop logging, `Ctrl+a`, `h`. +* Logout from screen. + +FreeRADIUS Project, copyright 2019 diff --git a/doc/antora/modules/developers/pages/coding-methods.adoc b/doc/antora/modules/developers/pages/coding-methods.adoc new file mode 100644 index 0000000..3de92c4 --- /dev/null +++ b/doc/antora/modules/developers/pages/coding-methods.adoc @@ -0,0 +1,235 @@ += Coding Methods + +The following is a short set of guidelines to follow while programming. It does +not address coding styles, function naming methods, or debugging methods. +Rather, it describes the processes which should go on in the programmer’s mind +while they are programming. + +Coding standards apply to function names, the look of the code, and coding +consistency. Coding methods apply to the daily practices used by the programmer +to write code. + +== Comment your code + +If you don’t, you’ll be forced to debug it six months later, when you have no clue +as to what it’s doing. + +If someone _really_ hates you, you’ll be forced to debug un-commented code that +someone else wrote. You don’t want to do that. + +For FreeRADIUS, use doxygen `@`-style comments so you get the +benefits of the developer documentation site, https://doc.freeradius.org/. + +== Give things reasonable names + +Variables and functions should have names. Calling them `x`, `xx`, +and `xxx` makes your life hell. Even `foo` and `i` are +problematic. + +Avoid smurfs. Don’t re-use struct names in field names, i. e. + +[source,c] +---- +struct smurf { + char *smurf_pappa_smurf; +} +---- + +If your code reads as full English sentences, you’re doing it right. + +== Check input parameters in the functions you write + +Your function _cannot_ do anything right if the user passed in garbage +and you were too lazy to check for garbage input. + +`assert()` (`fr_assert()`) is ugly. Use it. + +[NOTE] +==== +GIGO, "Garbage In, Garbage Out," is wrong. If your function gets +garbage input, it should complain loudly and with great +descriptiveness. +==== + +== Write useful error messages + +"Function failed" is useless as an error message. It makes debugging the code +impossible without source-level instrumentation. + +If you’re going to instrument the code at source level for error messages, leave +the error messages there, so the next sucker won’t have to do the same work all +over again. + +== Check error conditions from the functions you call + +Your function _cannot_ do anything right if you called another function, and +they gave you garbage output. + +One of the most common mistakes is: + +[source,c] +---- +fp = fopen(...); +fgetc(fp); /* core dumps! */ +---- + +If the programmer had bothered to check for a `NULL` fp (error +condition), then they could have produced a descriptive error message +instead of having the program core dump. + +== Core dumps are for weenies + +If your program core dumps accidentally, you’re a bad programmer. You don’t know +what your program is doing, or what it’s supposed to be doing when anything goes +wrong. + +If it hits an `assert()` and calls `abort()`, you’re a genius. You’ve thought +ahead to what _might_ go wrong, and put in an assertion to ensure that it fails +in a _known manner_ when something _does_ go wrong. (As it usually does…) + +== Initialize your variables + +`memset()` (`talloc_zero()`) is your friend. `ptr = NULL` is nice, +too. + +Having variables containing garbage values makes it easy for the code to do +garbage things. The contents of local variables are inputs to your function. See +xref:#_check_input_parameters_in_the_functions_you_write[#3]. + +It’s also nearly impossible for you to debug any problems, as you can’t tell the +variables with garbage values from the real ones. + +== Don’t allow buffer over-runs + +They’re usually accidental, but they cause core dumps. `strcpy()` and `strcat()` +are ugly. Use them under duress. + +`sizeof()` is your friend. + +== `const` is your friend + +If you don’t mean to modify an input structure to your function, declare it +`const`. Declare string constants `const`. It can’t hurt, and it allows more +errors to be found at compile time. + +Use `const` everywhere. Once you throw a few into your code, and have it save +you from stupid bugs, you’ll blindly throw in `const` everywhere. It’s a +life-saver. + +== Use C compiler warnings + +Turn on all of the C compiler warnings possible. You might have to turn some off +due to broken system header files, though. But the more warnings the merrier. + +Getting error messages at compile time is much preferable to getting core dumps +at run time. See xref:#_initialize_your_variables[#7]. + +Notice that the C compiler error messages are helpful? You should write error +messages like this, too. See xref:#_write_useful_error_messages[#4]. + +== Avoid UNIXisms and ASCIIisms and visualisms + +You don’t know under what system someone will try to run your code. Don’t demand +that others use the same OS or character set as you use. + +Never assign numbers to pointers. If foo is a `char*`, and you want it to be be +`NULL`, assign `NULL`, not `0`. The zeroth location is perfectly as addressable +as any other on plenty of OSes. Not all the world runs on Unix (though it should +:) ). + +Another common mistake is to assume that the zeroth character in the character +set is the string terminator. Instead of terminating a string with `0`, use +`'\0'`, which is always right. Similarly, `memset()` with the appropriate value: +`NULL`, `'\0'`, or `0` for pointers, chars, and numbers. + +Don’t put tabs in string constants, either. Always use `'\t'` to represent a +tab, instead of ASCII 9. Literal tabs are presented to readers of your code as +arbitrary whitespace, and it’s easy to mess up. + +== Make conditionals explicit + +Though it’s legal to test `if (foo) {}` if you test against the appropriate +value (like `NULL` or `'\0'`), your code is prettier and easier for others to +read without having to eyeball your prototypes continuously to figure out what +you’re doing (especially if your variables aren’t well-named). See +xref:#_give_things_reasonable_names[#2]. + +== Test your code + +Even Donald Knuth writes buggy code. You’ll never find all of the bugs in your +code. But writing a test program definitely helps. + +This means that you’ll have to write your code so that it will be easily +testable. As a result, it will look better and be easier to debug. + +== Hints, Tips, and Tricks + +This section lists many of the common `rules` associated with code submitted to +the project. There are always exceptions… but you must have a really good reason +for doing so. + +== Read the Documentation and follow the CodingStyle + +The FreeRADIUS server has a common coding style. Use real tabs to indent. There +is whitespace in variable assignments (`i = 1`, not `i=1`). + +When in doubt, format your code to look the same as code already in the server. +If your code deviates too much from the current style, it is likely to be +rejected without further review, and without comment. + +== #ifdefs are ugly + +Code cluttered with `#ifdef` s is difficult to read and maintain. Don’t do it. +Instead, put your `#ifdef` s in a header, and conditionally define `static +inline` functions, or macros, which are used in the code. Let the compiler +optimize away the 'no-op' case. + +Simple example, of poor code: + +[source,c] +---- +#ifdef CONFIG_MY_FUNKINESS + init_my_stuff(foo); +#endif +---- + +Cleaned-up example: + +(in header): + +[source,c] +---- +#ifndef CONFIG_MY_FUNKINESS + static inline void init_my_stuff(char *foo) {} +#endif +---- + +(in the code itself): + +[source,c] +---- +init_my_stuff(dev); +---- + +== `static inline` is better than a macro + +Static inline functions are greatly preferred over macros. They provide type +safety, have no length limitations, no formatting limitations, and under gcc +they are as cheap as macros. + +Macros should only be used for cases where a static inline is clearly suboptimal +(there a few, isolated cases of this in fast paths), or where it is impossible +to use a static inline function (such as string-izing). + +`static inline` is preferred over `$$static __inline__$$`, `extern inline`, and +`$$extern __inline__$$`. + +== Don’t over-design + +Don’t try to anticipate nebulous future cases which may or may not be useful: +_Make it as simple as you can, and no simpler._ + +Split up functionality as much as possible. If your code needs to do two +unrelated things, write two functions. Mashing two kinds of work into one +function makes the server difficult to debug and maintain. + diff --git a/doc/antora/modules/developers/pages/contributing.adoc b/doc/antora/modules/developers/pages/contributing.adoc new file mode 100644 index 0000000..e4a4b0a --- /dev/null +++ b/doc/antora/modules/developers/pages/contributing.adoc @@ -0,0 +1,126 @@ += Contributing + +== Submitting patches or diff’s to the FreeRADIUS project + +For a person or company wishing to submit a change to the FreeRADIUS +project the process can sometimes be daunting if you’re not familiar +with "the system." This text is a collection of suggestions which can +greatly increase the chances of your change being accepted. + +Note: Only trivial patches will be accepted via email. Large patches, or +patches that modify a number of files MUST be submitted as a +https://github.com/FreeRADIUS/freeradius-server/pulls[pull-request via GitHub]. + +== Hints and tips + +=== 1. Describe your changes + +Describe the technical detail of the change(s) your patch or commit +includes. + +Be as specific as possible. The WORST descriptions possible include +things like "update file X", "bug fix for file X", or "this patch +includes updates for subsystem X. Please apply." + +If your description starts to get long, that’s a sign that you probably +need to split up your commit. See the next point. + +=== 2. Separate your changes + +Separate each logical change into its own commit. + +For example, if your changes include both bug fixes and performance +enhancements for a single module, separate those changes into two or +more patches. + +On the other hand, if you make a single change to numerous files, group +those changes into a single commit. Thus a single LOGICAL change is +contained within a single commit. + +If one commit depends on another commit in order for a change to be +complete, that is OK. Simply note "this commit depends on commit X" in +the extended commit description. + +If your commit includes significant whitespace changes these should also +be broken out into another, separate, commit. + +== Submitting patches via GitHub + +See the following links for more details about submitting via github: + +* https://help.github.com/articles/fork-a-repo +* http://wiki.freeradius.org/contributing/GitHub + +== Submitting patches via email + +=== 1. diff -u + +Use `diff -u` or `diff -urN` to create patches. + +All changes to the source occur in the form of patches, as generated by +diff(1). When creating your patch, make sure to create it in unified +diff format, as supplied by the `-u` argument to diff(1). Patches +should be based in the root source directory, not in any lower +subdirectory. + +To create a patch for a single file, it is often sufficient to do:: + +``` +SRCTREE=/home/user/src/freeradiusd/ +MYFILE=src/modules/rlm_foo/foo.c + +cd $SRCTREE +cp $MYFILE $MYFILE.orig +vi $MYFILE # make your change +diff -u $MYFILE.orig $MYFILE > /tmp/patch +``` + +To create a patch for multiple files, you should unpack a `vanilla`, +or unmodified source tree, and generate a diff against your own source +tree. For example: + +``` +MYSRC=/home/user/src/freeradiusd-feature/ + +gunzip freeradiusd-version.tar.gz +tar xvf freeradiusd-version.tar +diff -urN freeradiusd-version $MYSRC > ~/feature-version.patch +``` + +=== 2. Select e-mail destination + +If you are on the developers mailing list, send the patch there. +mailto:freeradius-devel@lists.freeradius.org[freeradius-devel@lists.freeradius.org] + +Otherwise, send the patch to +mailto:patches@freeradius.org[patches@freeradius.org] + +=== 3. No MIME, no links, no compression, no attachments. Just plain text + +The developers need to be able to read and comment on the changes you +are submitting. It is important for a developer to be able to `quote` +your changes, using standard e-mail tools, so that they may comment on +specific portions of your code. + +For this reason, all patches should be submitting e-mail `inline`. + +Do not attach the patch as a MIME attachment, compressed or not. Many +popular e-mail applications will not always transmit a MIME attachment +as plain text, making it impossible to comment on your code. A MIME +attachment also takes a bit more time to process, decreasing the +likelihood of your MIME-attached change being accepted. + +Compressed patches are generally rejected outright. If the developer has +to do additional work to read your patch, the odds are that it will be +ignored completely. + +=== 4. E-mail size + +Large changes are not appropriate for mailing lists, and some +maintainers. If your patch, exceeds 5Kb in size, you must submit the +patch via GitHub instead. + +=== 5. Name the version of the server + +It is important to note, either in the subject line or in the patch +description, the server version to which this patch applies. diff --git a/doc/antora/modules/developers/pages/coverage.adoc b/doc/antora/modules/developers/pages/coverage.adoc new file mode 100644 index 0000000..af6048e --- /dev/null +++ b/doc/antora/modules/developers/pages/coverage.adoc @@ -0,0 +1,11 @@ += Code Coverage + +We use the link:https://gcc.gnu.org/onlinedocs/gcc/Gcov-Intro.html#Gcov-Intro[gcov] tool to know our tests coverage. + +[source,shell] +---- +$ make clean +$ make coverage +---- + +If completed with success, a pretty report will be available in `${source_tree}/build/coverage/index.html` diff --git a/doc/antora/modules/developers/pages/index.adoc b/doc/antora/modules/developers/pages/index.adoc new file mode 100644 index 0000000..e66ef61 --- /dev/null +++ b/doc/antora/modules/developers/pages/index.adoc @@ -0,0 +1,18 @@ += FreeRADIUS Development + +List with some usual howtos for FreeRADIUS. + +Programming reference documentation can be found at the +https://doc.freeradius.org/[Doxygen] site. + +== Topics + +* xref:bugs.adoc[Bugs] +* xref:coding-methods.adoc[Coding Methods] +* xref:coverage.adoc[Code Coverage] +* xref:profile.adoc[Profiling] +* xref:contributing.adoc[Contributing] +* xref:release-method.adoc[Release Method] + +Also see the xref:installation:dependencies.adoc[build +dependencies] page. diff --git a/doc/antora/modules/developers/pages/profile.adoc b/doc/antora/modules/developers/pages/profile.adoc new file mode 100644 index 0000000..ca94f53 --- /dev/null +++ b/doc/antora/modules/developers/pages/profile.adoc @@ -0,0 +1,41 @@ += Profiling + +Build with gperftools, and ensure that it's built: + +[source,shell] +---- +$ grep profile Make.inc +GPERFTOOLS_LIBS = -lprofiler +---- + +Run your test using the profiling tools. One signal will start the +profiling, another will stop it. + +[source,shell] +---- +env CPUPROFILE=/tmp/freeradius.prof CPUPROFILESIGNAL=12 freeradius ... +killall -12 freeradius + ... wait ... +killall -12 freeradius +---- + +View the results + +[source,shell] +---- +pprof --text /path/to/freeradius /tmp/freeradius.prof +---- + +or as a graph + +[source,shell] +---- +pprof --gv /path/to/freeradius /tmp/freeradius.prof +---- + +or as cachgrind output + +[source,shell] +---- +pprof --cachegrind /path/to/freeradius /tmp/freeradius.prof +---- diff --git a/doc/antora/modules/developers/pages/release-method.adoc b/doc/antora/modules/developers/pages/release-method.adoc new file mode 100644 index 0000000..1de9e2d --- /dev/null +++ b/doc/antora/modules/developers/pages/release-method.adoc @@ -0,0 +1,55 @@ += Release Method + +== Topics + +[arabic] +. As of 2.0, the release process is much simpler. Edit the Changelog +with the version number and any last updates. + +``` +# vi doc/ChangeLog +# git commit doc/ChangeLog +``` + +[arabic, start=2] +. Change version numbers in the VERSION file: + +``` +# vi VERSION +# git commit VERSION +``` + +[arabic, start=3] +. Make the files + +NOTE: It also does `make dist-check`, which checks the build rules for +various packages. + +``` +# make dist +``` + +[arabic, start=4] +. Validate that the packages are OK. If so, tag the release. + +NOTE: This does NOT actually do the tagging! You will have to run the +command it prints out yourself. + +``` +# make dist-tag +``` + +[arabic, start=5] +. Sign the packages. You will need the correct GPG key for this to work. + +``` +# make dist-sign +``` + +[arabic, start=6] +. Push to the FTP site. You will need write access to the FTP site for +this to work. + +``` +# make dist-publish +``` diff --git a/doc/antora/modules/howto/nav.adoc b/doc/antora/modules/howto/nav.adoc index 351200b..dab23f8 100644 --- a/doc/antora/modules/howto/nav.adoc +++ b/doc/antora/modules/howto/nav.adoc @@ -17,3 +17,6 @@ ***** xref:protocols/proxy/radsec_with_haproxy.adoc[Proxying RadSec with HAproxy] ***** xref:protocols/proxy/radsec_with_traefik.adoc[Proxying RadSec with Traefik] ***** xref:protocols/proxy/enable_proxy_protocol.adoc[Enabling PROXY Protocol for RadSec] +** xref:monitoring/index.adoc[Monitoring] +*** xref:monitoring/statistics.adoc[Server statistics] +** xref:simultaneous_use.adoc[Simultaneous-Use] diff --git a/doc/antora/modules/howto/pages/monitoring/index.adoc b/doc/antora/modules/howto/pages/monitoring/index.adoc new file mode 100644 index 0000000..a08ffb4 --- /dev/null +++ b/doc/antora/modules/howto/pages/monitoring/index.adoc @@ -0,0 +1,67 @@ += Monitoring + +Any good systems administrator will want to know how well +their systems are operating, both to catch issues before they +become a serious problem, or for long term analysis. +The term "monitoring" can encompass all kinds of watching how the +system is working, from generating and watching logs, gathering +statistics or ensuring that the service daemon is still running +and serving requests. + +We break the different types of monitoring down into the following +sections. + +== Service checking + +Checking the running service can include the following: + +* Ensuring the daemon is still running, i.e. process monitoring +* Sending regular RADIUS authentication or accounting requests and checking they are correctly responded to +* Sending Status-Server RADIUS requests + +Within a proxy environment FreeRADIUS needs to know if upstream +proxies are available. It can do this itself using the latter two +options above. + +== Logging + +System logs are often the most critical part of a RADIUS system. +They are necessary for the administrator to know who has logged in +and when, for debugging purposes such as when an end user cannot +connect, and often for regulatory or compliance purposes. + +RADIUS server logs are also often used as a basic form of +recording accounting requests, which are in and of themselves a +form of logging by the NAS. Getting correct logging systems +operational is key to running an efficient and easy to maintain +RADIUS server. + +FreeRADIUS has many options for being able to generate and store +logs, including the following: + +* Main daemon logging, configured in `radiusd.conf` +* Line-based text logging, using `rlm_linelog` +* Detailed RADIUS packet logs, using `rlm_detail` + +As well as recording direct to disk, the above can be sent via a +local syslog server, which opens up many opportunities for central +logging. + +It is possible to integrate FreeRADIUS into other more complicated +logging systems, some options may include: + +* To CSV files, for example via `rlm_linelog` +* Writing entries to an SQL database using `rlm_sql` +* Into a log management system such as Elasticsearch or Graylog + + +== Statistics gathering + +It is often useful to collect statistics from a running RADIUS +server. These are often plotted on graphs to show current load or +for trend analysis, as well as an indication of system operation. + +Statistics are usually gathered in two ways: + +* FreeRADIUS xref:monitoring/statistics.adoc[internal statistics] +* Analysing logs with some external tool diff --git a/doc/antora/modules/howto/pages/monitoring/statistics.adoc b/doc/antora/modules/howto/pages/monitoring/statistics.adoc new file mode 100644 index 0000000..0583f0a --- /dev/null +++ b/doc/antora/modules/howto/pages/monitoring/statistics.adoc @@ -0,0 +1,336 @@ += Server statistics + +FreeRADIUS collects statistics internally about certain operations +it is doing, such as the number of authentication and accounting +requests, how many accepts and failures, and server queue lengths. +These can be queried by sending a specially-crafted RADIUS +`Status-Server` packet to a "status" virtual server. + +== Configuring the status virtual server + +The `status` virtual server is present in the default +configuration, but needs to be enabled before it can be used. To +do this, create a symlink from `sites-enabled/status` to +`../sites-available/status`: + +[source,shell] +---- +# cd raddb/sites-enabled +# ln -s ../sites-available/status +---- + +[NOTE] +==== +If you are not starting from the default configuration, check that +`status_server` is still set to `yes` in `raddb/radiusd.conf` as +well. +==== + +While the default configuration will work for most setups, you may +edit the virtual server configuration in `sites-enabled/status`. +No major changes are necessary here, though the default secret, +`adminsecret`, should be changed. Other possible changes may be +the listening IP address and port, and the clients that are +allowed to connect. By default, connections are restricted to the +local host only. + +Having enabled and configured the status server, restart +FreeRADIUS to make it active. + +== Querying the server + +To get the current statistics from the server, send a RADIUS +request of type `Status-Server` to the status port. Unless edited +above, the request must come from the same server that FreeRADIUS +is running on, and be sent to port 18121 with the secret +'adminsecret' . At a minimum, the `FreeRADIUS-Statistics-Type` +attribute must be set. For example: + + $ cat <<EOF | radclient -x localhost:18121 status adminsecret + > FreeRADIUS-Statistics-Type = 0x01 + > Message-Authenticator = 0x00 + > EOF + Sent Status-Server Id 145 from 0.0.0.0:b852 to 127.0.0.1:18121 length 62 + FreeRADIUS-Statistics-Type = Authentication + Message-Authenticator = 0x00 + Received Access-Accept Id 145 from 127.0.0.1:46c9 to 127.0.0.1:47186 length 152 + FreeRADIUS-Total-Access-Requests = 27 + FreeRADIUS-Total-Access-Accepts = 20 + FreeRADIUS-Total-Access-Rejects = 1 + FreeRADIUS-Total-Access-Challenges = 0 + FreeRADIUS-Total-Auth-Responses = 5 + FreeRADIUS-Total-Auth-Duplicate-Requests = 0 + FreeRADIUS-Total-Auth-Malformed-Requests = 0 + FreeRADIUS-Total-Auth-Invalid-Requests = 0 + FreeRADIUS-Total-Auth-Dropped-Requests = 0 + FreeRADIUS-Total-Auth-Unknown-Types = 0 + FreeRADIUS-Total-Auth-Conflicts = 0 + +The `FreeRADIUS-Statistics-Type` attribute is a bitmask - add +together the following numbers to select the statistics required. +Some options are mutually exclusive, so it might be necessary to +send multiple requests to collect all information. + +[%header,cols="2,1,1,5"] +|=== +|Name|Hex value|Decimal value|Description + +|Authentication +|0x01 +|1 +|Stats about authentications + +|Accounting +|0x02 +|2 +|Stats about accounting + +|Proxy Auth +|0x04 +|4 +|Proxied authentication requests + +|Proxy Accounting +|0x08 +|8 +|Proxied accounting requests + +|Internal +|0x10 +|16 +|Queue lengths, thread information etc. + +|Client +|0x20 +|32 +|Statistics about RADIUS clients e.g. defined in `clients.conf` + +|Server +|0x40 +|64 +|Statistics about server 'listen' sockets e.g. in `sites-enabled/*` + +|Home Server +|0x80 +|128 +|Statistics about a proxy home servers e.g. in `proxy.conf` +|=== + +== Worked examples + +To show the statistics available, a few examples follow. + +=== Global server authentications + +Using `FreeRADIUS-Statistics-Type = 0x01` requests stats about +authentications. Because, for example, no "Client" qualifier has +been added (`0x20`) the numbers are global to the server. + +[source,shell] +---- +# cat <<EOF | radclient -x localhost:18121 status adminsecret +FreeRADIUS-Statistics-Type = 0x01 +Message-Authenticator=0x00 +EOF +Sent Status-Server Id 90 from 0.0.0.0:e008 to 127.0.0.1:18121 length 50 + FreeRADIUS-Statistics-Type = Authentication + Message-Authenticator = 0x00 +Received Access-Accept Id 90 from 127.0.0.1:46c9 to 127.0.0.1:57352 length 152 + FreeRADIUS-Total-Access-Requests = 133 + FreeRADIUS-Total-Access-Accepts = 114 + FreeRADIUS-Total-Access-Rejects = 13 + FreeRADIUS-Total-Access-Challenges = 0 + FreeRADIUS-Total-Auth-Responses = 127 + FreeRADIUS-Total-Auth-Duplicate-Requests = 0 + FreeRADIUS-Total-Auth-Malformed-Requests = 0 + FreeRADIUS-Total-Auth-Invalid-Requests = 0 + FreeRADIUS-Total-Auth-Dropped-Requests = 0 + FreeRADIUS-Total-Auth-Unknown-Types = 0 + FreeRADIUS-Total-Auth-Conflicts = 0 +---- + +=== Global server authentication and accounting requests + +Sending `0x01` requests authentication statistics, and `0x02` +requests accounting stats. To get both in one result, add them +together, so we requst `0x03`. In this example we send decimal +rather than hexadecimal. + +[source,shell] +---- +# cat <<EOF | radclient -x localhost:18121 status adminsecret +FreeRADIUS-Statistics-Type = 3 +Message-Authenticator=0x00 +EOF +Sent Status-Server Id 216 from 0.0.0.0:ce7b to 127.0.0.1:18121 length 50 + FreeRADIUS-Statistics-Type = Auth-Acct + Message-Authenticator = 0x00 +Received Access-Accept Id 216 from 127.0.0.1:46c9 to 127.0.0.1:52859 length 248 + FreeRADIUS-Total-Access-Requests = 542 + FreeRADIUS-Total-Access-Accepts = 451 + FreeRADIUS-Total-Access-Rejects = 81 + FreeRADIUS-Total-Access-Challenges = 0 + FreeRADIUS-Total-Auth-Responses = 532 + FreeRADIUS-Total-Auth-Duplicate-Requests = 0 + FreeRADIUS-Total-Auth-Malformed-Requests = 0 + FreeRADIUS-Total-Auth-Invalid-Requests = 0 + FreeRADIUS-Total-Auth-Dropped-Requests = 0 + FreeRADIUS-Total-Auth-Unknown-Types = 0 + FreeRADIUS-Total-Auth-Conflicts = 0 + FreeRADIUS-Total-Accounting-Requests = 0 + FreeRADIUS-Total-Accounting-Responses = 0 + FreeRADIUS-Total-Acct-Duplicate-Requests = 0 + FreeRADIUS-Total-Acct-Malformed-Requests = 0 + FreeRADIUS-Total-Acct-Invalid-Requests = 0 + FreeRADIUS-Total-Acct-Dropped-Requests = 0 + FreeRADIUS-Total-Acct-Unknown-Types = 0 + FreeRADIUS-Total-Acct-Conflicts = 0 +---- + +=== Internal server stats + +The value `0x10` requests information about the server such as queue +lengths and thread state. + +[source,shell] +---- +# cat <<EOF | radclient -x localhost:18121 status adminsecret +FreeRADIUS-Statistics-Type = 0x10 +Message-Authenticator=0x00 +EOF +Sent Status-Server Id 158 from 0.0.0.0:a090 to 127.0.0.1:18121 length 50 + FreeRADIUS-Statistics-Type = Internal + Message-Authenticator = 0x00 +Received Access-Accept Id 158 from 127.0.0.1:46c9 to 127.0.0.1:41104 length 164 + FreeRADIUS-Stats-Start-Time = "Aug 3 2023 13:36:24 UTC" + FreeRADIUS-Stats-HUP-Time = "Aug 3 2023 13:36:24 UTC" + FreeRADIUS-Queue-Len-Internal = 0 + FreeRADIUS-Queue-Len-Proxy = 0 + FreeRADIUS-Queue-Len-Auth = 0 + FreeRADIUS-Queue-Len-Acct = 0 + FreeRADIUS-Queue-Len-Detail = 0 + FreeRADIUS-Queue-PPS-In = 0 + FreeRADIUS-Queue-PPS-Out = 0 + FreeRADIUS-Stats-Threads-Active = 0 + FreeRADIUS-Stats-Threads-Total = 0 + FreeRADIUS-Stats-Threads-Max = 0 +---- + +=== Complete global server information + +A useful common request is all information about the server on a +global basis: internal stats (16 / `0x10`) plus authentications (1 +/ `0x01`), accounting (2 / `0x02`), proxy authentications (4 / +`0x04`) and proxy accounting (8 / `0x08`). The value `All` is +defined in the dictionary as `0x1f` (decimal 31) to cover +this common eventuality, and is what we demonstrate here. + +[source,shell] +---- +# cat <<EOF | radclient -x localhost:18121 status adminsecret +FreeRADIUS-Statistics-Type = All +Message-Authenticator=0x00 +EOF +Sent Status-Server Id 4 from 0.0.0.0:9ee4 to 127.0.0.1:18121 length 50 + FreeRADIUS-Statistics-Type = All + Message-Authenticator = 0x00 +Received Access-Accept Id 4 from 127.0.0.1:46c9 to 127.0.0.1:40676 length 596 + FreeRADIUS-Total-Access-Requests = 792 + FreeRADIUS-Total-Access-Accepts = 659 + FreeRADIUS-Total-Access-Rejects = 122 + FreeRADIUS-Total-Access-Challenges = 0 + FreeRADIUS-Total-Auth-Responses = 781 + FreeRADIUS-Total-Auth-Duplicate-Requests = 0 + FreeRADIUS-Total-Auth-Malformed-Requests = 0 + FreeRADIUS-Total-Auth-Invalid-Requests = 0 + FreeRADIUS-Total-Auth-Dropped-Requests = 0 + FreeRADIUS-Total-Auth-Unknown-Types = 0 + FreeRADIUS-Total-Auth-Conflicts = 0 + FreeRADIUS-Total-Accounting-Requests = 0 + FreeRADIUS-Total-Accounting-Responses = 0 + FreeRADIUS-Total-Acct-Duplicate-Requests = 0 + FreeRADIUS-Total-Acct-Malformed-Requests = 0 + FreeRADIUS-Total-Acct-Invalid-Requests = 0 + FreeRADIUS-Total-Acct-Dropped-Requests = 0 + FreeRADIUS-Total-Acct-Unknown-Types = 0 + FreeRADIUS-Total-Acct-Conflicts = 0 + FreeRADIUS-Total-Proxy-Access-Requests = 0 + FreeRADIUS-Total-Proxy-Access-Accepts = 0 + FreeRADIUS-Total-Proxy-Access-Rejects = 0 + FreeRADIUS-Total-Proxy-Access-Challenges = 0 + FreeRADIUS-Total-Proxy-Auth-Responses = 0 + FreeRADIUS-Total-Proxy-Auth-Duplicate-Requests = 0 + FreeRADIUS-Total-Proxy-Auth-Malformed-Requests = 0 + FreeRADIUS-Total-Proxy-Auth-Invalid-Requests = 0 + FreeRADIUS-Total-Proxy-Auth-Dropped-Requests = 0 + FreeRADIUS-Total-Proxy-Auth-Unknown-Types = 0 + FreeRADIUS-Total-Proxy-Accounting-Requests = 0 + FreeRADIUS-Total-Proxy-Accounting-Responses = 0 + FreeRADIUS-Total-Proxy-Acct-Duplicate-Requests = 0 + FreeRADIUS-Total-Proxy-Acct-Malformed-Requests = 0 + FreeRADIUS-Total-Proxy-Acct-Invalid-Requests = 0 + FreeRADIUS-Total-Proxy-Acct-Dropped-Requests = 0 + FreeRADIUS-Total-Proxy-Acct-Unknown-Types = 0 + FreeRADIUS-Stats-Start-Time = "Aug 3 2023 13:36:24 UTC" + FreeRADIUS-Stats-HUP-Time = "Aug 3 2023 13:36:24 UTC" + FreeRADIUS-Queue-Len-Internal = 0 + FreeRADIUS-Queue-Len-Proxy = 0 + FreeRADIUS-Queue-Len-Auth = 0 + FreeRADIUS-Queue-Len-Acct = 0 + FreeRADIUS-Queue-Len-Detail = 0 + FreeRADIUS-Queue-PPS-In = 0 + FreeRADIUS-Queue-PPS-Out = 0 + FreeRADIUS-Stats-Threads-Active = 0 + FreeRADIUS-Stats-Threads-Total = 0 + FreeRADIUS-Stats-Threads-Max = 0 +---- + +=== Client statistics + +Data can be provided about each RADIUS client defined in the +server. Note that this is for the client definition, not for each +client that connects - if a client definition has a wide netmask +and permits multiple clients to connect, the statistics will be +aggregate for all clients using that definition. + +[NOTE] +==== +It is not possible to request global server statistics +concurrently with client statistics as both use the same reply +attributes. +==== + +Here we request accounting data for one particular client by IP +address. + +[source,shell] +---- +# cat <<EOF | radclient -x localhost:18121 status adminsecret +FreeRADIUS-Statistics-Type = 0x2f +FreeRADIUS-Stats-Client-IP-Address = 172.16.0.10 +Message-Authenticator=0x00 +EOF +Sent Status-Server Id 194 from 0.0.0.0:d897 to 127.0.0.1:18121 length 62 + FreeRADIUS-Statistics-Type = 47 + FreeRADIUS-Stats-Client-IP-Address = 172.16.0.10 + Message-Authenticator = 0x00 +Received Access-Accept Id 194 from 127.0.0.1:46c9 to 127.0.0.1:55447 length 236 + FreeRADIUS-Stats-Client-IP-Address = 172.16.0.10 + FreeRADIUS-Total-Access-Requests = 1491 + FreeRADIUS-Total-Access-Accepts = 1240 + FreeRADIUS-Total-Access-Rejects = 246 + FreeRADIUS-Total-Access-Challenges = 0 + FreeRADIUS-Total-Auth-Responses = 1486 + FreeRADIUS-Total-Auth-Duplicate-Requests = 0 + FreeRADIUS-Total-Auth-Malformed-Requests = 0 + FreeRADIUS-Total-Auth-Invalid-Requests = 0 + FreeRADIUS-Total-Auth-Dropped-Requests = 0 + FreeRADIUS-Total-Auth-Unknown-Types = 0 + FreeRADIUS-Total-Accounting-Requests = 0 + FreeRADIUS-Total-Accounting-Responses = 0 + FreeRADIUS-Total-Acct-Duplicate-Requests = 0 + FreeRADIUS-Total-Acct-Malformed-Requests = 0 + FreeRADIUS-Total-Acct-Invalid-Requests = 0 + FreeRADIUS-Total-Acct-Dropped-Requests = 0 + FreeRADIUS-Total-Acct-Unknown-Types = 0 +---- diff --git a/doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc b/doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc index 949868d..ca4d98f 100644 --- a/doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc +++ b/doc/antora/modules/howto/pages/protocols/dhcp/policy_common_options.adoc @@ -1,6 +1,6 @@ == Configure common reply options -FreeRADIUS includes a powerful xref:unlang/index.adoc[policy language] called +FreeRADIUS includes a powerful xref:index.adoc[policy language] called "unlang". Statements in unlang may be used to call further policies, update attribute diff --git a/doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc b/doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc index b689824..a4ab3db 100644 --- a/doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc +++ b/doc/antora/modules/howto/pages/protocols/proxy/enable_proxy_protocol.adoc @@ -47,7 +47,7 @@ Now reload the HAproxy service: [source,shell] ---- service haproxy reload ---- +---- For Traefik, enable the PROXY Protocol on connections to the RadSec @@ -79,7 +79,7 @@ test authentication: [source,shell] ---- - echo "User-Name = bob" | radclient 127.0.0.1 auth testing123 +echo "User-Name = bob" | radclient 127.0.0.1 auth testing123 ---- You should expect to see the familiar output: diff --git a/doc/antora/modules/howto/pages/simultaneous_use.adoc b/doc/antora/modules/howto/pages/simultaneous_use.adoc new file mode 100644 index 0000000..b4a97ab --- /dev/null +++ b/doc/antora/modules/howto/pages/simultaneous_use.adoc @@ -0,0 +1,78 @@ += Simultaneous-Use checking + +There are a whole lot of pieces which have to work together for +`Simultaneous-Use` to work. In this guide, we assume that user +sessions are stored in SQL. + +For `Simultaneous-Use` to work. the server needs to know who is +online, which means that accounting must be configured and working. +Start off by checking the basics, independent of `Simultaneous-Use`. + +As always, start off with reading the debug output, and use that +information to answer a few questions. + +== Did the user get Access-Accept? + +*No* - Fix that. Make sure that the user can be authenticated! + +*Yes* - FreeRADIUS told the NAS to allow the user online. This usually + works, but perhaps the NAS disagreed, and still dropped the user. It happens. + +The only way you know that a user is actually online is to check the +accounting data. So we will do that next. + +== Did the server then get an Accounting-Request for that user? + +*No* - The NAS isn't sending accounting packets, Simultaneous-Use will never work. + +Go fix the NAS so that it sends accounting packets. + +*Yes* - The NAS is telling FreeRADIUS that it allowed the user online, + and the user has an active session. We now have to see where that data is stored. + +== Did the accounting data go into the radacct table? + +As always, Read the debug output. + +*No* - There is nothing in the debug output about radacct? Configure the server to write accounting data to SQL + +ou can use `radclient` to send fake accounting packets for testing. +Use a real accounting packet as a template for input to `radclient`, +but change the `User-Name` so that the tests don't affect real users. + + +*Yes* - You see successful `INSERT` or `UPDATE` lines in `radacct`. That's good! + +== One last check + +Double-check the radacct database using an SQL client. Just to be sure that the data is really there. + +== It is now set up correctly to track user sessions + +If all that works, then the server is set up correctly to authenticate +users, and to store their data in SQL. This is the foundation for +`Simultaneous-Use`. + +== Set Simultaneous-Use + +Then, configure the server to set `Simultaneous-Use=1`. That tells the server to enforce `Simultaneous-Use`. That configuration can go into the `files` module, `sql`, or whereever else you want. + +You will also need to configure the `default` virtual server to check session data in SQL. Look for `Simultaneous-Use` in `sites-available/default`. Uncomment the line containing `sql` + +== Double check that a user can still log in! + +Go through all of the above steps _again_, checking that the user can +log in, and that the server is receiving accounting packets. + +This time, also look for the debug output to contain: + +``` +# Executing section session from file ... +session { +``` + +That shows it is checking the `session` database. If all goes well, the next few lines after that should show that it is checking `sql`. + +If the above text doesn't appear, then the server isn't getting told to set `Simultaneous-Use = 1`. You will have to fix that before going to the next step. + +If the user has not logged in yet, you will see an `Access-Accept`. Otherwise, if the user already has an active session, the server should say that the user is being rejected due to multiple logins. diff --git a/doc/antora/modules/installation/pages/dependencies.adoc b/doc/antora/modules/installation/pages/dependencies.adoc index e910e76..3fb3667 100644 --- a/doc/antora/modules/installation/pages/dependencies.adoc +++ b/doc/antora/modules/installation/pages/dependencies.adoc @@ -1,9 +1,8 @@ = FreeRADIUS Dependencies Some external dependencies must be installed before building or -running FreeRADIUS. The core depends on two mandatory libraries: -`libtalloc` for memory management and `libkqueue` for event -handling. +running FreeRADIUS. For version 3, the core depends on one +mandatory library: `libtalloc` for memory management. Many of the modules also have optional dependencies. For example, the LDAP module requires LDAP client libraries to be installed @@ -29,30 +28,8 @@ https://talloc.samba.org/talloc/doc/html/index.html `# apt-get install libtalloc-dev` -*RedHat or CentOS* +*RedHat, CentOS, Rocky Linux or equivalent* ``` -# subscription-manager repos --enable rhel-7-server-optional-rpms -# yum install libtalloc-dev -``` - -=== kqueue - -Kqueue is an event / timer API originally written for BSD systems. -It is _much_ simpler to use than third-party event libraries. A -library, `libkqueue`, is available for Linux systems. - -*OSX* - -_kqueue is already available, there is nothing to install._ - -*Debian, Ubuntu and `dpkg`-based systems* - -`# apt-get install libkqueue-dev` - -*RedHat or CentOS* - -``` -# subscription-manager repos --enable rhel-7-server-optional-rpms -# yum install libkqueue-dev +# yum install libtalloc-devel ``` diff --git a/doc/antora/modules/unlang/nav.adoc b/doc/antora/modules/unlang/nav.adoc index 77be328..e2d68b3 100644 --- a/doc/antora/modules/unlang/nav.adoc +++ b/doc/antora/modules/unlang/nav.adoc @@ -35,7 +35,7 @@ ** xref:condition/index.adoc[Conditional Expressions] *** xref:condition/cmp.adoc[Comparisons] *** xref:condition/operands.adoc[Operands] -*** xref:condition/return_code.adoc[The Return Code Operator] +*** xref:condition/return_codes.adoc[The Return Code Operator] *** xref:condition/eq.adoc[The '==' Operator] *** xref:condition/and.adoc[The '&&' Operator] *** xref:condition/or.adoc[The '||' Operator] diff --git a/doc/antora/modules/unlang/pages/case.adoc b/doc/antora/modules/unlang/pages/case.adoc index ba2b5fe..2d5749d 100644 --- a/doc/antora/modules/unlang/pages/case.adoc +++ b/doc/antora/modules/unlang/pages/case.adoc @@ -14,7 +14,7 @@ outside of a xref:switch.adoc[switch] statement. The `<match>` text can be an attribute reference such as `&User-Name`, -or it can be a xref:type/string/index.adoc[string]. If the match +or it can be a xref:type/index.adoc[string]. If the match text is a dynamically expanded string, then the match is performed on the output of the string expansion. diff --git a/doc/antora/modules/unlang/pages/condition/index.adoc b/doc/antora/modules/unlang/pages/condition/index.adoc index b9d9d5f..10a84c8 100644 --- a/doc/antora/modules/unlang/pages/condition/index.adoc +++ b/doc/antora/modules/unlang/pages/condition/index.adoc @@ -18,7 +18,7 @@ Conditions are expressed using the following syntax: |===== | Syntax | Description | xref:attr.adoc[&Attribute-Name] | Check for attribute existence. -| xref:condition/return_code.adoc[rcode] | Check return code of a previous module. +| xref:condition/return_codes.adoc[rcode] | Check return code of a previous module. | xref:condition/operands.adoc[data] | Check value of data. | xref:condition/cmp.adoc[lhs OP rhs] | Compare two kinds of data. | xref:condition/para.adoc[( condition )] | Check sub-condition diff --git a/doc/antora/modules/unlang/pages/condition/operands.adoc b/doc/antora/modules/unlang/pages/condition/operands.adoc index 4a2d00b..5b19af3 100644 --- a/doc/antora/modules/unlang/pages/condition/operands.adoc +++ b/doc/antora/modules/unlang/pages/condition/operands.adoc @@ -11,7 +11,7 @@ integer ---- Any text not matching xref:attr.adoc[&Attribute-Name] or -xref:condition/return_code.adoc[Return Code] is interpreted as a value for a +xref:condition/return_codes.adoc[Return Code] is interpreted as a value for a particular xref:type/index.adoc[data type]. Double-quoted strings and back-quoted strings are dynamically expanded diff --git a/doc/antora/modules/unlang/pages/default.adoc b/doc/antora/modules/unlang/pages/default.adoc index 3b298f6..30912ef 100644 --- a/doc/antora/modules/unlang/pages/default.adoc +++ b/doc/antora/modules/unlang/pages/default.adoc @@ -14,7 +14,7 @@ outside of a xref:switch.adoc[switch] statement. The `<match>` text can be an attribute reference such as `&User-Name`, -or it can be a xref:type/string/index.adoc[string]. If the match +or it can be a xref:type/index.adoc[string]. If the match text is a dynamically expanded string, then the match is performed on the output of the string expansion. diff --git a/doc/antora/modules/unlang/pages/type/index.adoc b/doc/antora/modules/unlang/pages/type/index.adoc index 7d0d70f..f26489b 100644 --- a/doc/antora/modules/unlang/pages/type/index.adoc +++ b/doc/antora/modules/unlang/pages/type/index.adoc @@ -6,7 +6,7 @@ conditional expressions or when assigning a value to an attribute. == Using Data Types The server support a wide range of data types, as given in the -xref:unlang/type/all_types.adoc[list of data types] page. The choice +xref:type/all_types.adoc[list of data types] page. The choice of which data type applies is determined by the context in which that data type is used. This context is usually taken from an attribute which is being assigned a value. @@ -55,7 +55,7 @@ contains special characters that may otherwise confuse the parser. `"192.168.0.2"`:: xref:type/string/double.adoc[Double-quoted string]. + The contents of the string are dynamically expanded as described in -the xref:unlang/xlat/index.adoc[dynamic expansion] page. The +the xref:xlat/index.adoc[dynamic expansion] page. The resulting output is then interpreted as the given data type. `{backtick}/bin/echo 192.168.0.2{backtick}`:: xref:type/string/backticks.adoc[backtick-quoted string]. @@ -110,7 +110,7 @@ interpreted as the data type `ipaddr`, and not as the literal string `"192.0.2."`. For a full list of data types which can be used in a cast, please see -the xref:unlang/type/all_types.adoc[list of data types] page, and the +the xref:type/all_types.adoc[list of data types] page, and the "Basic Type Types" section. // Copyright (C) 2020 Network RADIUS SAS. Licenced under CC-by-NC 4.0. diff --git a/doc/antora/modules/unlang/pages/xlat/builtin.adoc b/doc/antora/modules/unlang/pages/xlat/builtin.adoc index f236a57..10d3148 100644 --- a/doc/antora/modules/unlang/pages/xlat/builtin.adoc +++ b/doc/antora/modules/unlang/pages/xlat/builtin.adoc @@ -782,7 +782,7 @@ Return named subcapture value from the last regular expression evaluated. Results of named capture groups are available using the `%{regex:<named capture group>}` expansion. They will also be accessible using the numbered expansions -described xref:builtin.adoc#_0_32[above]. +described xref:#_0_32[above]. Every time a regular expression is evaluated, whether it matches or not, the named capture group values will be cleared. diff --git a/doc/developer/contributing.rst b/doc/developer/contributing.rst index b40f98c..99a6d7a 100644 --- a/doc/developer/contributing.rst +++ b/doc/developer/contributing.rst @@ -92,7 +92,7 @@ example:: If you are on the developers mailing list, send the patch there. freeradius-devel@lists.freeradius.org -Otherwise, send the patch to 'patches@freeradius.org' +Otherwise, please use GitHub. 3. No MIME, no links, no compression, no attachments. Just plain text ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/man/man1/radclient.1 b/man/man1/radclient.1 index 229dcae..b83bee9 100644 --- a/man/man1/radclient.1 +++ b/man/man1/radclient.1 @@ -1,10 +1,11 @@ -.TH RADCLIENT 1 "22 March 2019" "" "FreeRADIUS Daemon" +.TH RADCLIENT 1 "21 May 2024" "" "FreeRADIUS Daemon" .SH NAME radclient - send packets to a RADIUS server, show reply .SH SYNOPSIS .B radclient .RB [ \-4 ] .RB [ \-6 ] +.RB [ \-b ] .RB [ \-c .IR count ] .RB [ \-d @@ -52,6 +53,13 @@ automatically encrypted before the packet is sent to the server. Use IPv4 (default) .IP \-6 Use IPv6 +.IP \-b +Enforce the Blast RADIUS checks. All replies to an Access-Request packet +must contain a Message-Authenticator as the first attribute. + +For compatibility with old servers, this flag is not set by default. +However, radclient still checks for the Blast RADIUS signature, and +discards packets which match the attack. .IP \-c\ \fIcount\fP Send each packet \fIcount\fP times. .IP \-d\ \fIraddb_directory\fP diff --git a/raddb/all.mk b/raddb/all.mk index a7f4f14..6ab8c78 100644 --- a/raddb/all.mk +++ b/raddb/all.mk @@ -13,11 +13,15 @@ DEFAULT_MODULES := always attr_filter chap date \ mschap ntlm_auth pap passwd preprocess radutmp realm \ replicate soh sradutmp totp unix unpack utf8 +ifneq "$(OPENSSL_LIBS)" "" +DEFAULT_MODULE += dpsk +endif + LOCAL_MODULES := $(addprefix raddb/mods-enabled/,$(DEFAULT_MODULES)) LOCAL_CERT_FILES := Makefile README.md xpextensions \ ca.cnf server.cnf inner-server.cnf \ - client.cnf bootstrap + client.cnf realms/README.md bootstrap # # We don't create the installed certs if we're building a package, @@ -32,7 +36,7 @@ endif LEGACY_LINKS := $(addprefix $(R)$(raddbdir)/,users huntgroups hints) -RADDB_DIRS := certs mods-available mods-enabled policy.d \ +RADDB_DIRS := certs certs/realms mods-available mods-enabled policy.d \ sites-available sites-enabled \ $(patsubst raddb/%,%,$(shell find raddb/mods-config -type d -print)) diff --git a/raddb/certs/Makefile b/raddb/certs/Makefile index c9fbc9e..34948bd 100644 --- a/raddb/certs/Makefile +++ b/raddb/certs/Makefile @@ -28,7 +28,7 @@ include passwords.mk # ###################################################################### .PHONY: all -all: index.txt serial dh ca server client +all: index.txt serial ca server client .PHONY: client client: client.pem diff --git a/raddb/clients.conf b/raddb/clients.conf index 60f9f4b..5f39ff1 100644 --- a/raddb/clients.conf +++ b/raddb/clients.conf @@ -9,6 +9,25 @@ # Define RADIUS clients (usually a NAS, Access Point, etc.). # +# There are a number of security practices which are critical in the +# modern era. +# +# * don't use RADIUS/UDP or RADIUS/TCP over the Internet. Use RADIUS/TLS. +# +# * If you do send RADIUS over UDP or TCP, don't send MS-CHAPv2. +# Anyone who can see the MS-CHAPv2 data can crack it in milliseconds. +# +# * use the "radsecret" program to generate secrets. It uses Perl (sorry). +# Every time you run it, it will generate a new strong secret. +# +# * don't create shared secrets yourself. Anything you create is likely to +# be in a "cracking" dictionary, and will allow a hobbyist attacker +# to crack the shared secret in a few minutes. +# +# * Don't trust anyone who tells you to ignore the above recommendations. +# + +# # Defines a RADIUS client. # # '127.0.0.1' is another name for 'localhost'. It is enabled by default, @@ -82,17 +101,33 @@ client localhost { # Quotation marks can be entered by escaping them, # e.g. "foo\"bar" # - # A note on security: The security of the RADIUS protocol + # A note on security: The security of the RADIUS protocol # depends COMPLETELY on this secret! We recommend using a - # shared secret that is composed of: + # shared secret that at LEAST 16 characters long. It should + # preferably be 32 characters in length. The secret MUST be + # random, and should not be words, phrase, or anything else + # that is recognisable. + # + # Computing power has increased enormously since RADIUS was + # first defined. A hobbyist with a high-end GPU can try ALL + # of the 8-character shared secrets in about a day. The + # security of shared secrets increases MUCH more with the + # length of the shared secret, than with number of different + # characters used in it. So don't bother trying to use + # "special characters" or anything else in an attempt to get + # un-guessable secrets. Instead, just get data from a secure + # random number generator, and use that. + # + # You should create shared secrets using a method like this: + # + # dd if=/dev/random bs=1 count=24 | base64 # - # upper case letters - # lower case letters - # numbers + # This process will give output which takes 24 random bytes, + # and converts them to 32 characters of ASCII. The output + # should be accepted by all RADIUS clients. # - # And is at LEAST 8 characters long, preferably 16 characters in - # length. The secret MUST be random, and should not be words, - # phrase, or anything else that is recognisable. + # You should NOT create shared secrets by hand. They will + # not be random. They will will be trivial to crack. # # The default secret below is only for testing, and should # not be used in any real environment. @@ -100,15 +135,45 @@ client localhost { secret = testing123 # - # Old-style clients do not send a Message-Authenticator - # in an Access-Request. RFC 5080 suggests that all clients - # SHOULD include it in an Access-Request. The configuration - # item below allows the server to require it. If a client - # is required to include a Message-Authenticator and it does - # not, then the packet will be silently discarded. + # The global configuration "security.require_message_authenticator" + # flag sets the default for all clients. That default can be + # over-ridden here, by setting it to a value. If no value is set, + # then the default from the "radiusd.conf" file is used. + # + # See that file for full documentation on the flag, along + # with allowed values and meanings. + # + # This flag exists solely for legacy clients which do not send + # Message-Authenticator in all Access-Request packets. We do not + # recommend setting it to "no". + # + # The number one way to protect yourself from the BlastRADIUS + # attack is to update all RADIUS servers, and then set this + # flag to "yes". If all RADIUS servers are updated, and if + # all of them have this flag set to "yes" for all clients, + # then your network is safe. You can then upgrade the + # clients when it is convenient, instead of rushing the + # upgrades. + # + # allowed values: yes, no, auto + # +# require_message_authenticator = no + + # + # The global configuration "security.limit_proxy_state" + # flag sets the default for all clients. That default can be + # over-ridden here, by setting it to "no". + # + # See that file for full documentation on the flag, along + # with allowed values,and meanings. + # + # This flag exists solely for legacy clients which do not send + # Message-Authenticator in all Access-Request packets. We do not + # recommend setting it to "no". + # + # allowed values: yes, no, auto # - # allowed values: yes, no - require_message_authenticator = no +# limit_proxy_state = yes # # The short name is used as an alias for the fully qualified diff --git a/raddb/mods-available/date b/raddb/mods-available/date index 25a64da..2d7d85e 100644 --- a/raddb/mods-available/date +++ b/raddb/mods-available/date @@ -33,3 +33,48 @@ date wispr2date { # default = no # utc = yes } + +# +# The date module also provides the %{time_since:} xlat, which +# makes it possible to both: +# - get the time since the epoch in seconds, milliseconds or +# microseconds; and +# - calculate the time elapsed since a given time. +# +# Syntax is: %{time_since:BASE[ (number|&attribute)]} +# where "BASE" is "s", "ms" or "us". +# +# Examples: +# %{time_since:s} +# - time in seconds since the epoch, same as %c +# +# %{time_since:s 1695753388} +# - time in seconds since Tue 26 Sep 19:36:28 BST 2023 +# (which is 1695753388 in UNIX time) +# +# %{time_since:s &Tmp-Integer-0} +# - Time since the number of seconds in Tmp-Integer-0 +# +# %{time_since:ms} +# - Milliseconds since the epoch +# +# %{time_since:us} +# - Microseconds since the epoch +# +# The provided attribute should be an Integer (or Integer64 for +# ms or us bases). However, other attributes will be converted if +# possible, with a warning given. The only one that might make +# sense is a Date attribute (which will be scaled appropriately +# according to the base, as Date is always in seconds). +# +# Primary usage would be for taking latenct measurements, for +# example to calculate the number of microseconds an LDAP call +# took: +# +# update request { +# &Tmp-Integer64-0 := %{time_since:us}" +# } +# ldap +# update request { +# &Tmp-Integer64-1 := %{time_since:us &Tmp-Integer64-0}" +# } diff --git a/raddb/mods-available/detail b/raddb/mods-available/detail index ccf65f9..665b677 100644 --- a/raddb/mods-available/detail +++ b/raddb/mods-available/detail @@ -94,6 +94,39 @@ detail { # # log_packet_header = yes + + # + # There are many, many, issues with dates being printed as + # humanly-readable strings. The server tries hard to both + # print and parse dates correctly, however this is not always + # possible. + # + # The detail files may be generated on one machine, and read + # on another. The two systems may have different languages, + # so the names of the month may not be parseable. The two + # systems may have different time zones. Time zone parsing + # is pretty much impossible, as there are multiple time zones + # with the same name! + # + # In some cases, the local libraries may not be able to + # correctly parse the time zone it printed! i.e. the system + # documentation for the C library time functions sometimes + # even says that the time zones are ignored, and the dates + # are parsed as UTC. + # + # All of these issues can be avoided by printing the dates as + # integer. In nearly all cases, the integer printed is + # exactly what was received in the packet. + # + # This may resolve some issues, but it's not perfect. The + # dates received by FreeRADIUS are sent by the NAS, and + # created on the NAS. So if the time on the NAS is wrong, + # the dates printed by FreeRADIUS will also be wrong. The + # only solution is to make sure that the NAS is using the + # correct time. + # +# dates_as_integer = yes + # # Certain attributes such as User-Password may be # "sensitive", so they should not be printed in the diff --git a/raddb/mods-available/dpsk b/raddb/mods-available/dpsk new file mode 100644 index 0000000..3cd8411 --- /dev/null +++ b/raddb/mods-available/dpsk @@ -0,0 +1,145 @@ +# -*- text -*- +# +# $Id$ + +# +# Calculate dynamic PSKs +# +# This module needs the following attributes as input: +# +# * control:Pre-Shared-Key - the PSK for the user +# * User-Name - the supplicant MAC in hex format, e.g. "abcdef012345" +# * Called-Station-MAC - the AP MAC in binary +# this attribute is set by the "rewrite_called_station_id" policy. +# * FreeRADIUS-802.1X-Anonce - from the AP +# * FreeRADIUS-802.1X-EAPoL-Key-Msg - from the AP +# +# Note that you MUST run the "rewrite_called_station_id" policy before calling this module. +# +# That policy MUST also create the Called-Station-MAC attribute. +# +# Then place the following configuration into the "authorize" section: +# +# authorize { +# ... +# rewrite_called_station_id +# +# update control { +# &PSK-Identity := "bob" +# &Pre-Shared-Key := "this-is-super-secret" +# } +# dpsk +# +# } +# +# And update the "authenticate" section to list the "dpsk" module: +# +# authenticate { +# ... +# dpsk +# ... +# } +# +# The module will return "fail" if the PSK is not correct. It will return "ok" +# if the PSK is correct. +# +# It also updates &reply:Pre-Shared-Key with the found key, along with +# &reply:PSK-Identity with the found identity. +# +# We STRONGLY RECOMMEND THAT NO ONE USE THIS MODULE. +# +# While it works, it needs to use a brute-force method to match MAC +# to PSK. That process is extremely slow, and scales very poorly. +# +# i.e. if you have 10 PSKs, it's not too bad. If you have 10,000 +# PSKs, then the module can comsume 100% of CPU trying to +# brute-force every PSK. +# +# This is a limitation of how DPSK works. There is no way to make it +# better. The only thing we've done is to add a cache which can help +# to minimize the amount of brute-force attempts. +# + +# +# The modules configuration. +# +dpsk { + # + # The maximum number of entries to cache. + # + # The cache is keyed by (supplicant MAC + SSID) + # + # The cache entry is the PSK-Identity and Pre-Sharedd-Key, + # and/or the PMK which are used to verify the information in + # the Access-Request. + # + cache_size = 1024 + + # + # The lifetime of an entry in the cache. + # + cache_lifetime = 86400 + + # + # PSKs can also be stored in a CSV file. The format of the file is: + # + # identity,psk,mac + # + # If there are commas in a field, then the field can be + # double quoted: "psk". + # + # The mac field is optional. If it exists, then that PSK + # will be used. It is highly recommended that the MAC *not* be placed + # into the CSV file. Instead, the MAC and PSK should be placed into a + # database. The server can then be configured to look up the MAC in the + # database, which returns the PSK. That way this module will only ever + # check one PSK, which is fast. + # + # i.e. the CSV file should only contain the small number of PSKs where + # you do not yet know the MAC. As soon as you know the MAC, you should + # put the MAC and PSK into a database, and then remove the MAC and PSK + # from the CSV file. + # + # NOTE: the file is opened and read from top to bottom for every + # new request which comes in. This process can be very slow! + # + # However, opening the file for every new request means that the + # server does not have to be reloaded when the file changes. Instead, + # the file can be generated, and then moved into place atomically: + # + # create csv file > psk.csv.new + # mv psk.csv.new psk.csv + # + # Any process which writes a new "psk.csv" file MUST NOT + # write to the file directly, as that will cause the dpsk + # module to read partial entries and fail. Instead, use "mv" + # to atomically overwrite the old file with a new one. + # + # Both "cache_size" and "filename" can be configured at the + # same time, which is recommended. When an entry in the file + # is found, the identity, PSK, and MAC are saved in the cache. + # + # If a cache entry is found, then the filename is NOT read. + # + # The resulting combination of features means that the module + # should be as fast as possible, given the limitations of DPSK. + # + # NOTE: Tests show that the module can do ~100K PSK / DPSK + # checks per second. This means that if you have 10,000 + # users and 10 packets a second, the system will be 100% busy + # checking PSKs. + # + # As a result, the DPSK functionality is scales poorly. It + # should be used only with a small number of PSKs (100s + # perhaps), and only at low packet rates. If the server is + # getting 1000 packets per second, then it can only handle + # 100 PSKs before running out of CPU. + # + # Using the cache will help substantially. But the cache is + # only in memory, which means that all cache entries are lost + # when the server restarts. As a result, the combination of + # number of PSKs and packet rates should be kept as low as + # possible. + # +# filename = "${modconfdir}/${..:name}/psk.csv" +} diff --git a/raddb/mods-available/eap b/raddb/mods-available/eap index ee9e539..d149707 100644 --- a/raddb/mods-available/eap +++ b/raddb/mods-available/eap @@ -33,6 +33,28 @@ eap { # timer_expire = 60 + # + # Some supplicants may misbehave by starting many thousands + # of EAP sessions, but never finishing them. These sessions + # can cause the server to hit 'max_sessions' very quickly. + # The 'timer_expire' configuration above does not help as + # much as it could, because the old (duplicate) session + # should be deleted as soon as the new one comes in. + # + # If you set the 'dedup_key' below, whenever the EAP module + # starts a new session, it will check for a previous session + # which has the same dedup key. If a previous session + # is found, it is deleted. + # + # Setting this configuration item may cause issues if the + # same device uses multiple EAP sessions at the same time. + # But that device behavior should be rare to non-existent. + # + # The configuration item is commented out so that upgrades + # do not change existing behavior. + # +# dedup_key = "%{Calling-Station-Id}" + # There are many EAP types, but the server has support # for only a limited subset. If the server receives # a request for an EAP type it does not support, then @@ -231,6 +253,9 @@ eap { # Directory where multiple CAs are stored. Both # "ca_file" and "ca_path" can be used at the same time. # + # Each file in this directory must contain one + # certificate, and ONLY one certificate. + # ca_path = ${cadir} # OpenSSL does not reload contents of ca_path dir over time. @@ -1112,4 +1137,54 @@ eap { # # virtual_server = inner-tunnel #} + + # EAP-TEAP + # + # The TEAP module implements the EAP-TEAP protocol + # + #teap { + # Point to the common TLS configuration + # + # tls = tls-common + + # default_eap_type = mschapv2 + + # If 'cipher_list' is set here, it will over-ride the + # 'cipher_list' configuration from the 'tls-common' + # configuration. The EAP-TEAP module has it's own + # over-ride for 'cipher_list' because the + # specifications mandata a different set of ciphers + # than are used by the other EAP methods. + # + # cipher_list though must include "ADH" for anonymous provisioning. + # This is not as straight forward as appending "ADH" alongside + # "DEFAULT" as "DEFAULT" contains "!aNULL" so instead it is + # recommended "ALL:!EXPORT:!eNULL:!SSLv2" is used + # + # cipher_list = "ALL:!EXPORT:!eNULL:!SSLv2" + + # PAC lifetime in seconds (default: seven days) + # + # pac_lifetime = 604800 + + # Authority ID of the server + # + # If you are running a cluster of RADIUS servers, you should make + # the value chosen here (and for "pac_opaque_key") the same on all + # your RADIUS servers. This value should be unique to your + # installation. We suggest using a domain name. + # + # authority_identity = "1234" + + # PAC Opaque encryption key (must be exactly 32 bytes in size) + # + # This value MUST be secret, and MUST be generated using + # a secure method, such as via 'openssl rand -hex 32' + # + # pac_opaque_key = "0123456789abcdef0123456789ABCDEF" + + # Same as for TTLS, PEAP, etc. + # + # virtual_server = inner-tunnel + #} } diff --git a/raddb/mods-available/json b/raddb/mods-available/json index 02a62ae..88f17c0 100644 --- a/raddb/mods-available/json +++ b/raddb/mods-available/json @@ -142,7 +142,7 @@ json { # .Example # # ``` -# %{json_encode:&request[*] !&reply[*] &control.User-Name} +# %{json_encode:&request[*] !&reply[*] &control:User-Name} # ``` # # #### Output format modes diff --git a/raddb/mods-available/ldap b/raddb/mods-available/ldap index 997d41e..d5838ff 100644 --- a/raddb/mods-available/ldap +++ b/raddb/mods-available/ldap @@ -41,7 +41,7 @@ ldap { # That will give you the LDAP information for 'user'. # # Group membership can be queried by using the above "ldapsearch" string, - # and adding "memberof" qualifiers. For ActiveDirectory, use: + # and adding "memberof" qualifiers. For Active Directory, use: # # ldapsearch ... '(&(objectClass=user)(sAMAccountName=user)(memberof=CN=group,${base_dn}))' # @@ -152,10 +152,10 @@ ldap { # LDAP "bind as user" configuration to check PAP passwords. # - # Active Directory needs "bind as user", which can be done by - # adding the following "if" statement to the authorize {} section - # of the virtual server, after the "ldap" module. For - # example: + # Active Directory (or Azure AD) needs "bind as user", which + # can be done by adding the following "if" statement to the + # authorize {} section of the virtual server, after the + # "ldap" module. For example: # # ... # ldap @@ -174,6 +174,23 @@ ldap { # "Auth-Type LDAP" in order to do an LDAP "bind as user", which will hand # the user name / password to AD for verification. # + # Note that this ONLY works if FreeRADIUS receives a + # User-Password attribute in the Access-Request packet. + # e.g. PAP, or TTLS/PAP. + # + # USING MS-CHAP OR PEAP/MS-CHAP WITH ACTIVE DIRECTORY OVER LDAP WILL NOT WORK. + # + # ** EVER ***. + # + # THERE IS NOTHING YOU CAN DO TO MAKE IT WORK. + # + # If you have a local Active Directory server, you can use + # Samba and ntlm_auth. See the "mschap" and "ntlm_auth" + # modules for more information. + # + # Unfortunately, you cannot use Samba with Azure AD. You + # MUST use PAP or TTLS/PAP. + # # # Name of the attribute that contains the user DN. diff --git a/raddb/mods-available/ldap_google b/raddb/mods-available/ldap_google index 03c98d3..9487c4b 100644 --- a/raddb/mods-available/ldap_google +++ b/raddb/mods-available/ldap_google @@ -21,7 +21,7 @@ # username and password. That username and password should be used # below. # -# Ensure the Goolge client configuration which is used for FreeRADIUS +# Ensure the Google client configuration which is used for FreeRADIUS # has sufficient permissions to read user information, and, if group # membership is part of the FreeRADIUS policy, ensure that the client # can read group information. This configuration is done on Google's diff --git a/raddb/mods-available/mschap b/raddb/mods-available/mschap index 1748d57..5fbdcee 100644 --- a/raddb/mods-available/mschap +++ b/raddb/mods-available/mschap @@ -51,9 +51,26 @@ mschap { # and the mschap module will do the authentication itself, # without calling ntlm_auth. # - # Be VERY careful when editing the following line! + # This authentication can go wrong for a number of reasons: + # 1) the user does not exist in AD + # 2) the password entered by the user is not the same as + # what is in AD + # 3) some magic MS-CHAP data is wrong. # - # You can also try setting the user name as: + # These situations can be checked by running ntlm_auth + # from the command line with a name and a password: + # + # ntlm_auth --username=NAME --password=PASSWORD + # + # If that works, you know both that the user exists, and the + # password is correct. You also know what AD expects for the + # username. + # + # There is often confusion between different formats of the + # username. Is it "user", or "user@domain" or "DOMAIN\\user"? + # The answer is "that depends on your local AD system". + # + # One solution is to use this for the username: # # ... --username=%{mschap:User-Name} ... # @@ -61,6 +78,23 @@ mschap { # attribute, and do prefix/suffix checks in order to obtain # the "best" user name for the request. # + # Another option is to use the Stripped-User-Name, as in the + # example configuration below. + # + # You can test which format works by running the server in + # debug mode, and copying the hex strings from the + # --challenge=... and --nt-response=... output. + # + # Then, run ntlm_auth from the command line, using the same + # command-line options as given below. Since you can't + # change the challenge or nt-response strings, try changing + # the --username=... and --domain=... parameters. Try + # different formats for them until one works. There should only + # be a small number of variations possible. + # + # That is the username and domain format which you need to + # configure here in this file. + # # For Samba 4, you should also set the "ntlm auth" parameter # in the Samba configuration: # diff --git a/raddb/mods-available/sql b/raddb/mods-available/sql index 0f435ad..68ac4da 100644 --- a/raddb/mods-available/sql +++ b/raddb/mods-available/sql @@ -291,6 +291,23 @@ sql { # # Setting 'max' to MORE than the number of threads means # that there are more connections than necessary. + # + # The setting here should be lower than the maximum + # number of connections allowed by the database. + # + # i.e. There is no point in telling FreeRADIUS to use + # 64 connections, while the database is limited to 32 + # connections. That configuration will cause the + # server to be "starved" of connections, and it will + # block during normal operations, even when the + # database is largely idle. + # + # At the same time, if the database is slow, there is + # no point in increasing "max". More connections + # will just cause the database to run more slowly. + # The correct fix for a slow database is to fix it, so + # that it responds to FreeRADIUS quickly. + # max = ${thread[pool].max_servers} # Spare connections to be left idle @@ -371,6 +388,21 @@ sql { # of the SQL module. group_attribute = "SQL-Group" + # When attributes read from the network are used in SQL queries + # their values are escaped to make them safe. + # By default FreeRADIUS uses its escaping routine which replaces + # unsafe characters with their mime-encoded equivalent. + # The list of safe characters is conservative, to allow for differences + # between different SQL implementations. + # + # If you are using the mysql or postgresql drivers, those have their + # own escaping functions which only escape characters as required + # by those databases. + # + # Set this option to yes to use the database driver provided escape + # function. +# auto_escape = no + # Read database-specific queries $INCLUDE ${modconfdir}/${.:name}/main/${dialect}/queries.conf } diff --git a/raddb/mods-available/sql_map b/raddb/mods-available/sql_map index 93b2636..a0b32ef 100644 --- a/raddb/mods-available/sql_map +++ b/raddb/mods-available/sql_map @@ -6,11 +6,6 @@ sql_map { # use the *instance* name here: sql1. sql_module_instance = "sql" - # This is duplicative of info available in the SQL module, but - # we have to list it here as we do not yet support nested - # reference expansions. - dialect = "mysql" - # Name of the check item attribute to be used as a key in the SQL queries query = "SELECT ... FROM ... " diff --git a/raddb/mods-available/totp b/raddb/mods-available/totp index 695365f..a68a317 100644 --- a/raddb/mods-available/totp +++ b/raddb/mods-available/totp @@ -13,6 +13,12 @@ # # &control:TOTP-Secret # +# Any "bare" key should be placed into: +# +# &control:TOTP-Key +# +# If TOTP-Key exists, then it will be used instead of TOTP-Secret. +# # The TOTP password entered by the user should be placed into: # # &request:TOTP-Password @@ -32,9 +38,44 @@ # https://linux.die.net/man/1/qrencode # # and then run that locally to get an image. -# # -# The module takes no configuration items. +# +# Some tokens get severely out of sync with local time. It is +# possible to offset the definition of "now" for one token by setting: +# +# &control:TOTP-Time-Offset := 120 +# +# This is a signed integer, with allowed values between -600 to +600. +# The offset is added to to the current time, to get the tokens idea +# of "now". # totp { + # + # Default time step between time changes + # + time_step = 30 + + # + # Length of the one-time password. + # + # Must be 6 or 8 + # + otp_length = 6 + + # + # How many steps backward in time we look for a matching OTP + # + lookback_steps = 1 + + # + # How many steps forward in time we look for a matching OTP + # + lookforward_steps = 0 + + # + # Time delta between steps. + # + # Cannot be larger than time_step + # + lookback_interval = 30 } diff --git a/raddb/mods-config/sql/counter/sqlite/dailycounter.conf b/raddb/mods-config/sql/counter/sqlite/dailycounter.conf index 9a2ec38..b95afdf 100644 --- a/raddb/mods-config/sql/counter/sqlite/dailycounter.conf +++ b/raddb/mods-config/sql/counter/sqlite/dailycounter.conf @@ -5,7 +5,7 @@ # below # query = "\ - SELECT SUM(acctsessiontime - GREATEST((%%b - strftime('%%s', acctstarttime)), 0)) \ + SELECT SUM(acctsessiontime - MAX((%%b - strftime('%%s', acctstarttime)), 0)) \ FROM radacct \ WHERE username = '%{${key}}' \ AND (strftime('%%s', acctstarttime) + acctsessiontime) > %%b" diff --git a/raddb/mods-config/sql/counter/sqlite/expire_on_login.conf b/raddb/mods-config/sql/counter/sqlite/expire_on_login.conf index f4e95a5..6c1c086 100644 --- a/raddb/mods-config/sql/counter/sqlite/expire_on_login.conf +++ b/raddb/mods-config/sql/counter/sqlite/expire_on_login.conf @@ -1,5 +1,5 @@ query = "\ - SELECT GREATEST(strftime('%%s', NOW()) - strftime('%%s', acctstarttime), 0) AS expires \ + SELECT MAX(strftime('%%s', NOW()) - strftime('%%s', acctstarttime), 0) AS expires \ FROM radacct \ WHERE username = '%{${key}}' \ ORDER BY acctstarttime \ diff --git a/raddb/mods-config/sql/counter/sqlite/monthlycounter.conf b/raddb/mods-config/sql/counter/sqlite/monthlycounter.conf index 5262097..3f5d427 100644 --- a/raddb/mods-config/sql/counter/sqlite/monthlycounter.conf +++ b/raddb/mods-config/sql/counter/sqlite/monthlycounter.conf @@ -5,7 +5,7 @@ # below # query = "\ - SELECT SUM(acctsessiontime - GREATEST((%%b - strftime('%%s', acctstarttime)), 0)) \ + SELECT SUM(acctsessiontime - MAX((%%b - strftime('%%s', acctstarttime)), 0)) \ FROM radacct \ WHERE username = '%{${key}}' AND \ (strftime('%%s', acctstarttime) + acctsessiontime) > %%b" diff --git a/raddb/mods-config/sql/counter/sqlite/weeklycounter.conf b/raddb/mods-config/sql/counter/sqlite/weeklycounter.conf index 06ce3b6..90a8566 100644 --- a/raddb/mods-config/sql/counter/sqlite/weeklycounter.conf +++ b/raddb/mods-config/sql/counter/sqlite/weeklycounter.conf @@ -5,7 +5,7 @@ # below # query = "\ - SELECT SUM(acctsessiontime - GREATEST((%%b - strftime('%%s', acctstarttime)), 0)) \ + SELECT SUM(acctsessiontime - MAX((%%b - strftime('%%s', acctstarttime)), 0)) \ FROM radacct \ WHERE username = '%{${key}}' \ AND (strftime('%%s', acctstarttime) + acctsessiontime) > %%b" diff --git a/raddb/mods-config/sql/cui/mysql/schema.sql b/raddb/mods-config/sql/cui/mysql/schema.sql index da9b2f7..01cc615 100644 --- a/raddb/mods-config/sql/cui/mysql/schema.sql +++ b/raddb/mods-config/sql/cui/mysql/schema.sql @@ -1,4 +1,4 @@ -CREATE TABLE `cui` ( +CREATE TABLE IF NOT EXISTS `cui` ( `clientipaddress` varchar(46) NOT NULL default '', `callingstationid` varchar(50) NOT NULL default '', `username` varchar(64) NOT NULL default '', diff --git a/raddb/mods-config/sql/dhcp/mysql/queries.conf b/raddb/mods-config/sql/dhcp/mysql/queries.conf index a28037b..b0254e5 100644 --- a/raddb/mods-config/sql/dhcp/mysql/queries.conf +++ b/raddb/mods-config/sql/dhcp/mysql/queries.conf @@ -69,7 +69,7 @@ authorize_group_reply_query = "\ ORDER BY id" group_membership_query = "\ - SELECT groupnme \ + SELECT groupname \ FROM ${dhcpgroup_table} \ WHERE identifier='%{SQL-User-Name}' AND context = '%{control:DHCP-SQL-Option-Context}' \ ORDER BY priority" diff --git a/raddb/mods-config/sql/ippool-dhcp/mysql/schema.sql b/raddb/mods-config/sql/ippool-dhcp/mysql/schema.sql index d8b1219..f996ba3 100644 --- a/raddb/mods-config/sql/ippool-dhcp/mysql/schema.sql +++ b/raddb/mods-config/sql/ippool-dhcp/mysql/schema.sql @@ -5,7 +5,7 @@ -- that is much faster. -- -CREATE TABLE dhcpippool ( +CREATE TABLE IF NOT EXISTS dhcpippool ( id int unsigned NOT NULL auto_increment, pool_name varchar(30) NOT NULL, framedipaddress varchar(15) NOT NULL default '', diff --git a/raddb/mods-config/sql/ippool-dhcp/sqlite/schema.sql b/raddb/mods-config/sql/ippool-dhcp/sqlite/schema.sql index 339d58d..f7af667 100644 --- a/raddb/mods-config/sql/ippool-dhcp/sqlite/schema.sql +++ b/raddb/mods-config/sql/ippool-dhcp/sqlite/schema.sql @@ -9,7 +9,7 @@ CREATE TABLE dhcpstatus ( INSERT INTO dhcpstatus (status_id, status) VALUES (1, 'dynamic'), (2, 'static'), (3, 'declined'), (4, 'disabled'); CREATE TABLE dhcpippool ( - id int(11) PRIMARY KEY, + id INTEGER PRIMARY KEY, pool_name varchar(30) NOT NULL, framedipaddress varchar(15) NOT NULL default '', pool_key varchar(30) NOT NULL default '', diff --git a/raddb/mods-config/sql/ippool/mongo/queries.conf b/raddb/mods-config/sql/ippool/mongo/queries.conf index 9d7d070..eedf0a0 100644 --- a/raddb/mods-config/sql/ippool/mongo/queries.conf +++ b/raddb/mods-config/sql/ippool/mongo/queries.conf @@ -22,12 +22,6 @@ # parser. # -# -# TBD -# -on_begin = "" -off_begin = "" - allocate_begin = "" # @@ -92,17 +86,11 @@ allocate_clear = "db.mypool_collection.findAndModify( \ allocate_commit = "" -start_begin = "" start_update = "" -start_commit = "" -stop_begin = "" stop_clear = "" -stop_commit = "" -alive_begin = "" alive_update = "" -alive_commit = "" on_clear = "" off_clear = "" diff --git a/raddb/mods-config/sql/ippool/oracle/queries.conf b/raddb/mods-config/sql/ippool/oracle/queries.conf index 1a64b28..9704f56 100644 --- a/raddb/mods-config/sql/ippool/oracle/queries.conf +++ b/raddb/mods-config/sql/ippool/oracle/queries.conf @@ -13,11 +13,6 @@ skip_locked = "" allocate_begin = "commit" -start_begin = "commit" -alive_begin = "commit" -stop_begin = "commit" -on_begin = "commit" -off_begin = "commit" # # Attempt to allocate the address a client previously had. This is based on pool_key diff --git a/raddb/mods-config/sql/ippool/sqlite/schema.sql b/raddb/mods-config/sql/ippool/sqlite/schema.sql index b020c62..4dc25d1 100644 --- a/raddb/mods-config/sql/ippool/sqlite/schema.sql +++ b/raddb/mods-config/sql/ippool/sqlite/schema.sql @@ -2,7 +2,7 @@ -- Table structure for table 'radippool' -- CREATE TABLE radippool ( - id int(11) PRIMARY KEY, + id INTEGER PRIMARY KEY, pool_name varchar(30) NOT NULL, framedipaddress varchar(15) NOT NULL default '', nasipaddress varchar(15) NOT NULL default '', diff --git a/raddb/mods-config/sql/main/mongo/queries.conf b/raddb/mods-config/sql/main/mongo/queries.conf index 732e1e8..a496932 100644 --- a/raddb/mods-config/sql/main/mongo/queries.conf +++ b/raddb/mods-config/sql/main/mongo/queries.conf @@ -91,7 +91,7 @@ authorize_check_query = "db.${authcheck_table}.aggregate([ \ 'op': ':=' \ } \ } \ -])" \ +])" # TBD: fill in things here authorize_reply_query = "" @@ -150,7 +150,7 @@ accounting { }, \ '$push': { \ 'events_data': { \ - 'event_id': '%{sha256:%{tolower:%{Calling-Station-Id}', \ + 'event_id': '%{sha256:%{tolower:%{Calling-Station-Id}}}', \ 'event_type': 'Accounting-Start', \ 'event_time': '%{Packet-Original-Timestamp}', \ 'creation_date': { '$date': { '$numberLong': '%{expr: (%l * 1000) + (%M / 1000)}' } } \ @@ -202,7 +202,7 @@ accounting { }, \ '$push': { \ 'events_data': { \ - 'event_id': '%{sha256:%{tolower:%{Calling-Station-Id}', \ + 'event_id': '%{sha256:%{tolower:%{Calling-Station-Id}}}', \ 'event_type': 'Accounting-Interim-Update', \ 'event_time': '%{Packet-Original-Timestamp}', \ 'creation_date': { '$date': { '$numberLong': '%{expr: (%l * 1000) + (%M / 1000)}' } } \ @@ -214,7 +214,7 @@ accounting { 'closed': false, \ 'creation_date': { '$date': { '$numberLong': '%{expr: (%l * 1000) + (%M / 1000)}' } } \ } \ - }, + }, \ 'upsert': true \ })" # End Interim-Update @@ -235,7 +235,7 @@ accounting { }, \ '$push': { \ 'events_data': { \ - 'event_id': '%{sha256:%{tolower:%{Calling-Station-Id}', \ + 'event_id': '%{sha256:%{tolower:%{Calling-Station-Id}}}', \ 'event_type': 'Accounting-Stop', \ 'event_time': '%{Packet-Original-Timestamp}', \ 'creation_date': { '$date': { '$numberLong': '%{expr: (%l * 1000) + (%M / 1000)}' } } \ diff --git a/raddb/mods-config/sql/main/mssql/queries.conf b/raddb/mods-config/sql/main/mssql/queries.conf index 1978463..d83a27d 100644 --- a/raddb/mods-config/sql/main/mssql/queries.conf +++ b/raddb/mods-config/sql/main/mssql/queries.conf @@ -373,7 +373,7 @@ accounting { UPDATE ${....acct_table1} \ SET \ AcctStartTime = ${....event_timestamp}, \ - AcctUpdateTime = ${....event_timestamp }, \ + AcctUpdateTime = ${....event_timestamp}, \ AcctStartDelay = '%{%{Acct-Delay-Time}:-0}', \ ConnectInfo_start = '%{Connect-Info}' \ WHERE AcctUniqueId = '%{Acct-Unique-Session-ID}' \ diff --git a/raddb/mods-config/sql/main/mysql/extras/wimax/schema.sql b/raddb/mods-config/sql/main/mysql/extras/wimax/schema.sql index e32224a..bc2e7da 100644 --- a/raddb/mods-config/sql/main/mysql/extras/wimax/schema.sql +++ b/raddb/mods-config/sql/main/mysql/extras/wimax/schema.sql @@ -3,7 +3,7 @@ # which replaces the "radpostauth" table. # -CREATE TABLE wimax ( +CREATE TABLE IF NOT EXISTS wimax ( id int(11) NOT NULL auto_increment, username varchar(64) NOT NULL default '', authdate timestamp NOT NULL, diff --git a/raddb/mods-config/sql/main/mysql/process-radacct.sql b/raddb/mods-config/sql/main/mysql/process-radacct.sql index 8902338..0696603 100644 --- a/raddb/mods-config/sql/main/mysql/process-radacct.sql +++ b/raddb/mods-config/sql/main/mysql/process-radacct.sql @@ -45,7 +45,7 @@ -- +----------------+----------------+-----------------+ -- 7 rows in set (0.000 sec) -- -CREATE TABLE data_usage_by_period ( +CREATE TABLE IF NOT EXISTS data_usage_by_period ( username VARCHAR(64), period_start DATETIME, period_end DATETIME, diff --git a/raddb/mods-config/sql/main/postgresql/queries.conf b/raddb/mods-config/sql/main/postgresql/queries.conf index 18a1ed0..80953e0 100644 --- a/raddb/mods-config/sql/main/postgresql/queries.conf +++ b/raddb/mods-config/sql/main/postgresql/queries.conf @@ -534,7 +534,7 @@ accounting { '%{%{NAS-IPv6-Address}:-%{NAS-IP-Address}}', \ NULLIF('%{%{NAS-Port-ID}:-%{NAS-Port}}', ''), \ '%{NAS-Port-Type}', \ - ${....event_timestamp}, \ + TO_TIMESTAMP(${....event_timestamp_epoch} - %{%{Acct-Session-Time}:-0}), \ ${....event_timestamp}, \ NULL, \ %{%{Acct-Session-Time}:-NULL}, \ diff --git a/raddb/mods-config/sql/moonshot-targeted-ids/mysql/schema.sql b/raddb/mods-config/sql/moonshot-targeted-ids/mysql/schema.sql index 8a33dc1..bca2ba8 100644 --- a/raddb/mods-config/sql/moonshot-targeted-ids/mysql/schema.sql +++ b/raddb/mods-config/sql/moonshot-targeted-ids/mysql/schema.sql @@ -1,4 +1,4 @@ -CREATE TABLE `moonshot_targeted_ids` ( +CREATE TABLE IF NOT EXISTS `moonshot_targeted_ids` ( `gss_acceptor` varchar(254) NOT NULL default '', `namespace` varchar(36) NOT NULL default '', `username` varchar(64) NOT NULL default '', diff --git a/raddb/policy.d/canonicalization b/raddb/policy.d/canonicalization index 6d90e37..1b2f6e3 100644 --- a/raddb/policy.d/canonicalization +++ b/raddb/policy.d/canonicalization @@ -77,6 +77,7 @@ rewrite_called_station_id { if (&Called-Station-Id && (&Called-Station-Id =~ /^${policy.mac-addr-regexp}([^0-9a-f](.+))?$/i)) { update request { &Called-Station-Id := "%{toupper:%{1}-%{2}-%{3}-%{4}-%{5}-%{6}}" + &Called-Station-MAC := "0x%{toupper:%{1}%{2}%{3}%{4}%{5}%{6}}" } # SSID component? diff --git a/raddb/proxy.conf b/raddb/proxy.conf index 26f620c..cf0697d 100644 --- a/raddb/proxy.conf +++ b/raddb/proxy.conf @@ -252,6 +252,24 @@ home_server localhost { # secret = testing123 + # + # The global configuration "security.require_message_authenticator" + # flag sets the default for all home servers. That default can be + # over-ridden here, by setting it to a value. If no value is set, + # then the default from the "radiusd.conf" file is used. + # + # See that file for full documentation on the flag, along + # with allowed values and meanings. + # + # This flag exists solely for legacy home servers which do + # not send Message-Authenticator in all Access-Accept, + # Access-Reject, or Access-Challenge packets. We do not + # recommend setting it to "no". + # + # allowed values: yes, no, auto + # +# require_message_authenticator = no + ############################################################ # # The rest of the configuration items listed here are optional, diff --git a/raddb/radiusd.conf.in b/raddb/radiusd.conf.in index 366dce4..44fee62 100644 --- a/raddb/radiusd.conf.in +++ b/raddb/radiusd.conf.in @@ -270,6 +270,27 @@ hostname_lookups = no #postauth_client_lost = no # +# Some NASes will aggressively retransmit packets, and cause a DoS of +# the RADIUS infrastructure. They should follow he recommended +# retransmission behavior of RFC 5080 Section 2.2.2, but it seems +# that only (some) RADIUS servers follow that guidance. +# +# When a duplicate packet is received from the NAS, the server will +# see when the last retransmission was done. If it is within the +# "proxy_dedup_window", the retransmitted packet is dropped. +# +# i.e. There is zero benefit to sending the same RADIUS packet +# multiple times in one second. There is, in fact, serious harm +# in doing so. Aggressive retransmissions can result in network +# congestion, and ultimately failure of the RADIUS infrastructure. +# +# This behavior *cannot* be disabled. +# +# Allowed values here are 1..10. Only integers are supported. +# +#proxy_dedup_window = 1 + +# # Logging section. The various "log_*" configuration items # will eventually be moved here. # @@ -424,6 +445,16 @@ ENV { # # BAR + + # + # If the server needs kerberos credentials, then they can be placed + # into the following keytab file. + # + # This also permits the server to use those credentials when it is + # run in debug mode. + # +# KRB5_CLIENT_KTNAME = ${raddbdir}/radiusd.keytab + # # `LD_PRELOAD` is special. It is normally set before the # application runs, and is interpreted by the dynamic linker. @@ -572,6 +603,191 @@ security { # status_server = yes + # + # Global configuration for requiring Message-Authenticator in + # all Access-* packets sent over UDP or TCP. This flag is + # ignored for TLS. + # + # The number one way to protect yourself from the BlastRADIUS + # attack is to update all RADIUS servers, and then set this + # flag to "yes". If all RADIUS servers are updated, and if + # all of them have this flag set to "yes" for all clients, + # then your network is safe. You can then upgrade the + # clients when it is convenient, instead of rushing the + # upgrades. + # + # This flag sets the global default for all clients and home + # servers. It can be over-ridden in an individual client or + # home_server definition by adding the same flag to that + # section with an appropriate value. + # + # All upgraded RADIUS implementations should send + # Message-Authenticator in all Access-Request, Access-Accept, + # Access-Reject, and Access-Challenge packets. Once all + # systems are upgraded, setting this flag to "yes" is the + # best protection from the attack. + # + # The possible values and meanings for + # "require_message_authenticator" are; + # + # * "no" - allow Access-* packet which do not contain + # Message-Authenticator + # + # For a client, if this flag is set to "no", then the + # "limit_proxy_state" flag, below, is also checked. + # + # For a home_server, if this flag is set to "no", then the + # Access-Accept, Access-Reject, and Access-Challenge + # packets do not need to contain Message-Authenticator. + # + # The only reason to set this flag to "no" is when the + # RADIUS client or home server has not been updated. It is + # always safer to set this flag "no" in the individual + # client or home_server definition. The global flag SHOULD + # still be set to a safe value: "yes". + # + # WARNING: Setting this flag and the "limit_proxy_state" + # flag to "no" will allow MITM attackers to create fake + # Access-Accept packets to the NAS! At least one of them + # MUST be set to "yes" for the system to have any + # protection against the attack. + # + # * "yes" - Require that all Access-* packets (client and + # home_server) contain Message-Authenticator. If a packet + # does not contain Message-Authenticator, then it is + # discarded. + # + # * "auto" - Automatically determine the value of the flag, + # based on the first packet received from that client or + # home_server. + # + # If the packet does not contain Message-Authenticator, + # then the value of the flag is automatically switched to + # "no". + # + # If the packet contains Message-Authenticator but not + # EAP-Message, then the value of the flag is automatically + # switched to "yes". The server has to check for + # EAP-Message, because the previous RFCs require that the + # packet contains Message-Authenticator when it also + # contains EAP-Message. So having a Message-Authenticator + # in those packets doesn't give the server enough + # information to determined if the client or home_server + # has been updated. + # + # If the packet contains Message-Authenticator and + # EAP-Message, then the flag is left at the "auto" value. + # + # WARNING: This switch is done for the first packet + # received from that client or home server. The change + # does NOT persist across server restarts. You MUST change + # the to "yes" manually, in order to make a permanent + # change to the configuration. + # + # WARNING: If there are multiple NASes with the same source + # IP and client definitions, BUT the NASes have different + # behavior, then this flag WILL LIKELY BREAK YOUR NETWORK. + # + # That is, when there are multiple different RADIUS clients + # behind one NATed IP address, then these security settings + # have to be set to allow the MOST INSECURE packets to be + # processed. This is a terrible idea, and will leave your + # network vulnerable to the attack. Please upgrade all + # clients immediately. + # + # The only solution to that rare configuration is to set + # this flag to "no", in which case the network will work, + # but will be vulnerable to the attack. + # + require_message_authenticator = auto + + # + # Global configuration for limiting the combination of + # Proxy-State and Message-Authenticator. This flag only + # applies to packets sent over UDP or TCP. This flag is + # ignored for TLS. + # + # This flag sets the global default for all clients. It can + # be over-ridden in an individual client definition by adding + # the same flag to that section with an appropriate value. + # + # If "require_message_authenticator" is set to "yes", this + # configuration item is ignored. + # + # If "require_message_authenticator" is set to "no", this + # configuration item is checked. + # + # The possible values and meanings for "limit_proxy_state" are; + # + # * "no" - allow any packets from the client, even packets + # which contain the BlastRADIUS attack. Please be aware + # that in this configuration the server will complain for + # EVERY packet which it receives. + # + # The only reason to set this flag to "no" is when the + # client is a proxy, AND the proxy does not send + # Message-Authenticator in Access-Request packets. Even + # then, the best approach to fix the issue is to (1) update + # the proxy to send Message-Authenticator, and if that + # can't be done, then (2) set this flag to "no", but ONLY + # for that one client. The global flag SHOULD still be set + # to a safe value: "yes". + # + # WARNING: Setting both this flag and the + # "require_message_authenticator" flag to "no" will allow + # MITM attackers to create fake Access-Accept packets to the + # NAS! At least one of them MUST be set to "yes" for the + # system to have any protection against the attack. + # + # * "yes" - Allow packets without Message-Authenticator, + # but only when they do not contain Proxy-State. + # packets which contain Proxy-State MUST also contain + # Message-Authenticator, otherwise they are discarded. + # + # This setting is safe for most NASes, GGSNs, BRAS, etc. + # Most regular RADIUS clients do not send Proxy-State + # attributes for Access-Request packets that they originate. + # However some aggregators (e.g. Wireless LAN Controllers) + # may act as a RADIUS proxy for requests from their cohort + # of managed devices, and in such cases will provide a + # Proxy-State attribute. For those systems, you _must_ look + # at the actual packets to determine what to do. It may be + # that the only way to fix the vulnerability is to upgrade + # the WLC, and set "require_message_authenticator" to "yes". + # + # * "auto" - Automatically determine the value of the flag, + # based on the first packet received from that client. + # + # If the packet contains Proxy-State but no + # Message-Authenticator, then the value of the flag is + # automatically switched to "no". + # + # For all other situations, the value of the flag is + # automatically switched to "yes". + # + # WARNING: This switch is done for the first packet + # received from that client. The change does NOT persist + # across server restarts. You MUST change the to "yes" + # manually, in order to make a permanent change to the + # configuration. + # + # WARNING: If there are multiple NASes with the same source + # IP and client definitions, BUT the NASes have different + # behavior, then this flag WILL LIKELY BREAK YOUR NETWORK. + # + # That is, when there are multiple different RADIUS clients + # behind one NATed IP address, then these security settings + # have to be set to allow the MOST INSECURE packets to be + # processed. This is a terrible idea, and will leave your + # network vulnerable to the attack. Please upgrade all + # clients immediately. + # + # The only solution to that rare configuration is to set + # this flag to "no", in which case the network will work, + # but will be vulnerable to the attack. + # + limit_proxy_state = auto + @openssl_version_check_config@ } diff --git a/raddb/sites-available/aws-nlb b/raddb/sites-available/aws-nlb index acea81e..06ca632 100644 --- a/raddb/sites-available/aws-nlb +++ b/raddb/sites-available/aws-nlb @@ -33,6 +33,15 @@ listen { proto = tcp ipaddr = * port = 8000 + + # + # Set limits so that unused connections get cleaned up quickly. + # + limit { + max_connections = 16 + lifetime = 5 + idle_timeout = 5 + } } # diff --git a/raddb/sites-available/default b/raddb/sites-available/default index 78b7ae7..b4339bd 100644 --- a/raddb/sites-available/default +++ b/raddb/sites-available/default @@ -348,6 +348,20 @@ authorize { digest # + # The dpsk module implements dynamic PSK. + # + # If the request contains FreeRADIUS-802.1X-Anonce + # and FreeRADIUS-802.1X-EAPoL-Key-Msg, then it will set + # &control:Auth-Type := dpsk + # + # The "rewrite_called_station_id" policy creates the + # Called-Station-MAC attribute, which is needed by + # the dpsk module. + # +# rewrite_called_station_id +# dpsk + + # # The WiMAX specification says that the Calling-Station-Id # is 6 octets of the MAC. This definition conflicts with # RFC 3580, and all common RADIUS practices. If you are using @@ -534,6 +548,8 @@ authenticate { pap } +# dpsk + # # Most people want CHAP authentication # A back-end database listed in the 'authorize' section diff --git a/raddb/sites-available/inner-tunnel b/raddb/sites-available/inner-tunnel index c178baa..1197e08 100644 --- a/raddb/sites-available/inner-tunnel +++ b/raddb/sites-available/inner-tunnel @@ -194,7 +194,7 @@ authorize { # LDAP servers can only do PAP. They cannot do CHAP, MS-CHAP, # or EAP. # -# if (!&control.Auth-Type && &User-Password) { +# if (!&control:Auth-Type && &User-Password) { # update control { # &Auth-Type := LDAP # } @@ -409,6 +409,13 @@ post-auth { &Module-Failure-Message := &request:Module-Failure-Message } } + + # + # Access-Challenge packets are sent through the Challenge sub-section + # of the post-auth section. + # + #Post-Auth-Type Challenge { + #} } # diff --git a/raddb/sites-available/tls b/raddb/sites-available/tls index 137fcbc..6eab1fe 100644 --- a/raddb/sites-available/tls +++ b/raddb/sites-available/tls @@ -56,12 +56,15 @@ listen { # type = auth+acct - # For now, only TCP transport is allowed. + # For now, only TCP transport is allowed. proto = tcp - # Send packets to the default virtual server + # Send packets to the default virtual server virtual_server = default + # + # We have clients specifically for TLS. + # clients = radsec # @@ -88,6 +91,22 @@ listen { # proxy_protocol = no # + # This configuration item should be enabled for all listen + # sections which do TLS. + # + # It is only disabled because we are careful about changing + # existing behavior in a stable release. + # + # Setting this configuration item to "yes" means that the + # server will be able to gracefully recover if a TLS + # connection is blocking at the network layer. + # + # Note that setting "nonblock = yes" is NOT possible for bare + # TCP connections. RADIUS/TCP should generally be avoided. + # +# nonblock = yes + + # # When this is set to "yes", new TLS connections # are processed through a section called # @@ -310,6 +329,11 @@ listen { tls_max_version = "1.3" # + # See mods-available/eap for documentation + # + ecdh_curve = "" + + # # Session resumption / fast reauthentication # cache. # @@ -514,6 +538,22 @@ home_server tls { proto = tcp status_check = none + # + # This configuration item should be enabled for all + # home_server sections which do TLS. + # + # It is only disabled because we are careful about changing + # existing behavior in a stable release. + # + # Setting this configuration item to "yes" means that the + # server will be able to gracefully recover if a TLS + # connection is blocking at the network layer. + # + # Note that setting "nonblock = yes" is NOT possible for bare + # TCP connections. RADIUS/TCP should generally be avoided. + # +# nonblock = yes + tls { # # Similarly to HTTP, the client can use Server Name diff --git a/redhat/freeradius.spec b/redhat/freeradius.spec index 3cb211f..69dd646 100644 --- a/redhat/freeradius.spec +++ b/redhat/freeradius.spec @@ -7,6 +7,7 @@ %{!?_with_rlm_eap_tnc: %global _without_rlm_eap_tnc --without-rlm_eap_tnc} %{!?_with_rlm_yubikey: %global _without_rlm_yubikey --without-rlm_yubikey} %{?_without_ldap: %global _without_libfreeradius_ldap --without-libfreeradius-ldap} +%{?el7: %global _without_rlm_eap_teap --without-rlm_eap_teap} # experimental modules %bcond_with rlm_idn @@ -30,7 +31,7 @@ Summary: High-performance and highly configurable free RADIUS server Name: freeradius -Version: 3.2.3 +Version: 3.2.5 Release: 1%{?dist} License: GPLv2+ and LGPLv2+ Group: System Environment/Daemons @@ -60,11 +61,12 @@ BuildRequires: autoconf BuildRequires: gdbm-devel BuildRequires: openssl, openssl-devel BuildRequires: pam-devel +BuildRequires: pcre-devel BuildRequires: zlib-devel BuildRequires: net-snmp-devel BuildRequires: net-snmp-utils -%{?el7:BuildRequires: libwbclient-devel} -%{?el7:BuildRequires: samba-devel} +BuildRequires: libwbclient-devel +BuildRequires: samba-devel %if %{?_unitdir:1}%{!?_unitdir:0} BuildRequires: systemd-devel %endif @@ -82,10 +84,9 @@ Requires: libpcap Requires: readline Requires: libtalloc Requires: net-snmp -%{?el7:Requires: libwbclient} +Requires: libwbclient Requires: zlib Requires: pam -%{?el6:Requires: redhat-lsb-core} %if %{?_with_rlm_idn:1}%{?!_with_rlm_idn:0} Requires: libidn @@ -193,13 +194,7 @@ Summary: Perl support for FreeRADIUS Group: System Environment/Daemons Requires: %{name} = %{version}-%{release} Requires: perl(:MODULE_COMPAT_%(eval "`%{__perl} -V:version`"; echo $version)) -%{?fedora:BuildRequires: perl-devel} -%if 0%{?rhel} <= 5 -BuildRequires: perl -%endif -%if 0%{?rhel} >= 6 BuildRequires: perl-devel -%endif BuildRequires: perl(ExtUtils::Embed) %description perl @@ -441,7 +436,7 @@ make %_smp_mflags rm -rf $RPM_BUILD_ROOT mkdir -p $RPM_BUILD_ROOT/var/run/radiusd mkdir -p $RPM_BUILD_ROOT/var/lib/radiusd -make install R=$RPM_BUILD_ROOT +make install R=$RPM_BUILD_ROOT PACKAGE='redhat' # modify default configuration RADDB=$RPM_BUILD_ROOT%{_sysconfdir}/raddb perl -i -pe 's/^#user =.*$/user = radiusd/' $RADDB/radiusd.conf @@ -639,6 +634,7 @@ fi %{_libdir}/freeradius/rlm_detail.so %{_libdir}/freeradius/rlm_dhcp.so %{_libdir}/freeradius/rlm_digest.so +%{_libdir}/freeradius/rlm_dpsk.so %{_libdir}/freeradius/rlm_dynamic_clients.so %{_libdir}/freeradius/rlm_eap.so %{_libdir}/freeradius/rlm_eap_fast.so @@ -647,6 +643,9 @@ fi %{_libdir}/freeradius/rlm_eap_mschapv2.so %{_libdir}/freeradius/rlm_eap_peap.so %{_libdir}/freeradius/rlm_eap_sim.so +%if 0%{?rhel} >= 8 +%{_libdir}/freeradius/rlm_eap_teap.so +%endif %{_libdir}/freeradius/rlm_eap_tls.so %{_libdir}/freeradius/rlm_eap_ttls.so %{_libdir}/freeradius/rlm_exec.so @@ -698,7 +697,13 @@ fi %attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/trigger.conf %config(noreplace) %{_sysconfdir}/raddb/users %dir %attr(770,root,radiusd) %{_sysconfdir}/raddb/certs -%attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/certs/* +%attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/certs/README.md +%attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/certs/Makefile +%attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/certs/bootstrap +%attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/certs/xpextensions +%attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/certs/*.cnf +%dir %attr(770,root,radiusd) %{_sysconfdir}/raddb/certs/realms +%attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/certs/realms/* %attr(750,root,radiusd) %{_sysconfdir}/raddb/certs/bootstrap %dir %attr(750,root,radiusd) %{_sysconfdir}/raddb/sites-available %attr(640,root,radiusd) %config(noreplace) %{_sysconfdir}/raddb/sites-available/* @@ -840,6 +845,7 @@ fi /usr/bin/radeapclient /usr/bin/radlast /usr/bin/radtest +/usr/bin/radsecret /usr/bin/radsniff /usr/bin/radsqlrelay /usr/bin/raduat diff --git a/scripts/ci/eapol_test-build.sh b/scripts/ci/eapol_test-build.sh index 42397e2..9e1dcd6 100755 --- a/scripts/ci/eapol_test-build.sh +++ b/scripts/ci/eapol_test-build.sh @@ -109,6 +109,9 @@ fi cp "$BUILD_CONF_FILE" "$WPA_SUPPLICANT_DIR/.config" +# Don't crash out on build warnings, some newer GCC versions are quite picky +sed -i -e 's/-Werror//' "${WPA_SUPPLICANT_DIR}/Makefile" + if ! make -C "${WPA_SUPPLICANT_DIR}" -j8 eapol_test 1>&2 || [ ! -e "${WPA_SUPPLICANT_DIR}/eapol_test" ]; then echo "Build error" 1>&2 if [ -z "${BUILD_DIR}" ]; then rm -rf "$TMP_BUILD_DIR"; fi diff --git a/scripts/ci/package-test.mk b/scripts/ci/package-test.mk index 417784b..b20e4d4 100644 --- a/scripts/ci/package-test.mk +++ b/scripts/ci/package-test.mk @@ -38,4 +38,5 @@ MAKE_ARGS := RADIUSD_BIN=$(RADIUSD_BIN) PORT=$(PORT) SECRET="$(SECRET)" DICT_PAT .PHONY: package-test package-test: + cp -r $(RADDB_PATH)/certs/* raddb/certs $(MAKE) -C src/tests $(MAKE_ARGS) tests.eap diff --git a/scripts/crossbuild/README.md b/scripts/crossbuild/README.md index 0bcc2c4..a5dc205 100644 --- a/scripts/crossbuild/README.md +++ b/scripts/crossbuild/README.md @@ -8,6 +8,9 @@ different operating systems, using Docker. The primary purpose is for developers to easily test FreeRADIUS on different systems. +**Do not use this for running FreeRADIUS in production - see +`scripts/docker` instead.** + ## Common Usage @@ -24,7 +27,7 @@ least on the first run): make crossbuild -or for the most common systems (Debian, Ubuntu, CentOS): +or for the most common systems (Debian, Ubuntu, CentOS, Rocky): make crossbuild.common @@ -41,7 +44,9 @@ The Docker containers are left running, and may be stopped with make crossbuild.down The system tries to be as efficient as possible, so will not -rebuild from scratch every time. +rebuild the Docker images from scratch every time, but use an +existing image and copy just the latest git commits in for +testing. ## Global make targets @@ -49,47 +54,47 @@ rebuild from scratch every time. The following targets will operate on the crossbuild system globally, or on all images (unless otherwise stated): + - `make crossbuild` -### `make crossbuild` - -Create all docker images (if required), start them, build and test -FreeRADIUS. + Create all docker images (if required), start them, build and + test FreeRADIUS. -### `make crossbuild.common` + - `make crossbuild.common` -As `make crossbuild`, but only build and test the most common -systems. + As `make crossbuild`, but only build and test the most common + systems. -### `make crossbuild.info` + - `make crossbuild.info` -List all systems, together with the expected state. See -`crossbuild.reset`. + List all systems, together with the expected state. See + `crossbuild.reset`. -### `make crossbuild.down` + - `make crossbuild.down` -Stop all containers. + Stop all containers. -### `make crossbuild.reset` + - `make crossbuild.reset` -If containers are stopped or started outside Docker, crossbuild -may get confused. This will clear the internal state which should -try and start everything from be beginning again. + If containers are stopped or started outside Docker, + crossbuild may get confused. This will clear the internal + state which should try and start everything from be beginning + again. -### `make crossbuild.clean` + - `make crossbuild.clean` -Bring down all containers, clear state. This is a general "tidy -up". + Bring down all containers, clear state. This is a general + "tidy up". -### `make crossbuild.wipe` + - `make crossbuild.wipe` -Don't just stop, but destroy all crossbuild docker images. This -will mean they need to be recreated again upon next use. + Don't just stop, but destroy all crossbuild docker images. + This will mean they need to be recreated again upon next use. ## Per-image make targets @@ -116,7 +121,16 @@ Docker images will be created with names in the form: freeradius-build/debian10 -whil containers will have names like: +while containers will have names like: fr-crossbuild-debian10 + +## Re-generating Dockerfiles + +The Dockerfiles used for crossbuild are generated from m4 +templates. To regenerate one use `make crossbuild.IMAGE.regen`, or +`make crossbuild.regen` to generate them all. The m4 templates are +stored in `scripts/crossbuild/m4/`. This will usually only need to +be used to add a new operating system, not during standard build +testing. diff --git a/scripts/crossbuild/crossbuild.mk b/scripts/crossbuild/crossbuild.mk index da96506..599a34c 100644 --- a/scripts/crossbuild/crossbuild.mk +++ b/scripts/crossbuild/crossbuild.mk @@ -11,13 +11,19 @@ else # # Short list of common builds # -CB_COMMON:=centos7 debian10 ubuntu18 +CB_COMMON:=centos7 rocky9 debian11 ubuntu20 + +# Where to put stamp files (subdirectory of where this makefile is) +CB_DIR:=$(dir $(realpath $(lastword $(MAKEFILE_LIST)))) # Where the docker directories are -DT:=scripts/crossbuild/docker +DT:=$(CB_DIR)/docker # Where to put stamp files (subdirectory of where this makefile is) -DD:=$(dir $(realpath $(lastword $(MAKEFILE_LIST))))/build +DD:=$(CB_DIR)/build + +# Location of top-level m4 template +DOCKER_TMPL:=$(CB_DIR)/../docker/m4/Dockerfile.m4 # List of all the docker images (sorted for "crossbuild.info") CB_IMAGES:=$(sort $(patsubst $(DT)/%,%,$(wildcard $(DT)/*))) @@ -99,6 +105,11 @@ crossbuild.clean: $(foreach IMG,${CB_IMAGES},crossbuild.${IMG}.clean) crossbuild.wipe: $(foreach IMG,${CB_IMAGES},crossbuild.${IMG}.wipe) # +# Regenerate all Dockerfiles from m4 templates +# +crossbuild.regen: $(foreach IMG,${CB_IMAGES},crossbuild.${IMG}.regen) + +# # Define rules for building a particular image # define CROSSBUILD_IMAGE_RULE @@ -145,6 +156,7 @@ crossbuild.${1}.up: $(DD)/stamp-up.${1} $(DD)/docker.refresh.${1}: $(DD)/stamp-up.${1} ${Q}echo "REFRESH ${1}" ${Q}docker container exec $(CB_CPREFIX)${1} sh -c 'rsync -a /srv/src/ /srv/local-src/' + ${Q}docker container exec $(CB_CPREFIX)${1} sh -c 'git config --global --add safe.directory /srv/local-src' ${Q}docker container exec $(CB_CPREFIX)${1} sh -c 'git config -f /srv/local-src/config core.bare true' ${Q}docker container exec $(CB_CPREFIX)${1} sh -c 'git config -f /srv/local-src/config --unset core.worktree || true' ${Q}docker container exec $(CB_CPREFIX)${1} sh -c '[ -d /srv/build ] || git clone /srv/local-src /srv/build' @@ -218,6 +230,16 @@ crossbuild.${1}.wipe: crossbuild.${1}.refresh: $(DD)/docker.refresh.${1} # +# Regenerate the image Dockerfile from the m4 templates +# +.PHONY: crossbuild.${1}.regen +crossbuild.${1}.regen: $(DT)/${1}/Dockerfile + +$(DT)/${1}/Dockerfile: $(DOCKER_TMPL) $(CB_DIR)/m4/Dockerfile.deb.m4 $(CB_DIR)/m4/Dockerfile.rpm.m4 + ${Q}echo REGEN ${1} + ${Q}m4 -I $(CB_DIR)/m4 -D D_NAME=${1} -D D_TYPE=crossbuild $$< > $$@ + +# # Run the build test # .PHONY: crossbuild.${1} diff --git a/scripts/crossbuild/docker/centos7/Dockerfile b/scripts/crossbuild/docker/centos7/Dockerfile index 2f9e4ac..91f25b3 100644 --- a/scripts/crossbuild/docker/centos7/Dockerfile +++ b/scripts/crossbuild/docker/centos7/Dockerfile @@ -1,16 +1,32 @@ -FROM centos:centos7 +# Auto generated for centos7 +# from scripts/crossbuild/m4/Dockerfile.rpm.m4 +# +# Rebuild this file with `make crossbuild.centos7.regen` +# +ARG from=centos:7 +FROM ${from} as build + +# +# CentOS 7 is now EOL, so we need to fix up the repo source +# +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* + # # Install devtools like make and git and the EPEL # repository for freetds and hiredis # RUN yum update -y -RUN yum install -y rpmdevtools openssl epel-release git yum-utils rsync +RUN yum install -y rpmdevtools openssl epel-release git procps yum-utils \ + rsync # # Install GCC that has the requisite support for C11 keywords and atomics # RUN yum install -y centos-release-scl +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* RUN yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++ ENV CC=/opt/rh/devtoolset-8/root/usr/bin/gcc @@ -21,6 +37,8 @@ ENV CC=/opt/rh/devtoolset-8/root/usr/bin/gcc RUN rm /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo RUN rm /etc/yum.repos.d/CentOS-SCLo-scl.repo + + # # Documentation build dependecies # @@ -28,12 +46,11 @@ RUN rm /etc/yum.repos.d/CentOS-SCLo-scl.repo # - doxygen & JSON.pm RUN yum install -y doxygen graphviz perl-JSON # - antora (npm needed) -RUN curl -sL https://rpm.nodesource.com/setup_10.x | bash - +RUN curl -sL https://rpm.nodesource.com/setup_16.x | bash - RUN yum install -y nodejs -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 # - pandoc RUN curl -o - -L $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*tar.gz" | cut -d '"' -f 4) | tar xzvf - -C /tmp/ -# " RUN mv /tmp/pandoc-*/bin/* /usr/local/bin # - asciidoctor RUN yum install -y rubygems-devel @@ -67,6 +84,7 @@ RUN git clone --depth 1 --no-single-branch ${source} # # Install build dependencies for all branches from v3 onwards # Nodesource has issues (no SRPMS in some repos) and is not needed here +# CentOS/RHEL 7 do not support "-D" for yum-builddep so do that separately below if needed # WORKDIR freeradius-server RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ @@ -75,6 +93,11 @@ RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin [ -e redhat/freeradius.spec ] && yum-builddep --disablerepo="nodesource*" -y redhat/freeradius.spec; \ done +# Yubikey deps for CentOS/RHEL 7 +RUN yum install -y ykclient-devel ykclient + +RUN yum install -y libyubikey-devel + # # Which is required by fixture setup utilities # @@ -85,8 +108,8 @@ RUN yum install -y which # RUN yum install -y libnl3-devel + # # Create the RPM build tree # -ENV BUILDDIR=/root/rpmbuild RUN rpmdev-setuptree diff --git a/scripts/crossbuild/docker/debian10/Dockerfile b/scripts/crossbuild/docker/debian10/Dockerfile index 3eb13a7..03a9ce5 100644 --- a/scripts/crossbuild/docker/debian10/Dockerfile +++ b/scripts/crossbuild/docker/debian10/Dockerfile @@ -1,8 +1,10 @@ -FROM debian:buster - -ARG gccver=8 -ARG clangver=8 -ARG osname=buster +# Auto generated for debian10 +# from scripts/crossbuild/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make crossbuild.debian10.regen` +# +ARG from=debian:buster +FROM ${from} as build ARG DEBIAN_FRONTEND=noninteractive @@ -15,14 +17,14 @@ RUN apt-get update && \ rm -r /var/lib/apt/lists/* # For clang -RUN add-apt-repository -y "deb http://apt.llvm.org/${osname}/ llvm-toolchain-${osname}-${clangver} main" && \ +RUN add-apt-repository -y "deb http://apt.llvm.org/buster/ llvm-toolchain-buster-8 main" && \ apt-key adv --fetch-keys http://apt.llvm.org/llvm-snapshot.gpg.key RUN apt-get update && \ # Development utilities apt-get install -y devscripts equivs git quilt rsync && \ # Compilers - apt-get install -y g++-${gccver} llvm-${clangver} clang-${clangver} lldb-${clangver} && \ + apt-get install -y g++ llvm-8 clang-8 lldb-8 && \ # eapol_test dependencies apt-get install -y libnl-3-dev libnl-genl-3-dev @@ -33,9 +35,9 @@ RUN apt-get update && \ # - doxygen & JSON.pm RUN apt-get install -y doxygen graphviz libjson-perl # - antora (npm needed) -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - RUN apt-get install -y nodejs -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 # - pandoc WORKDIR /tmp RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) @@ -44,15 +46,13 @@ RUN apt-get install -y ./pandoc-*.deb RUN apt-get install -y ruby-dev RUN gem install asciidoctor -# set default things -RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${gccver} 50 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-${gccver} && \ - update-alternatives --config gcc - -RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${clangver} 60 && \ +# +# Set defaults +# +RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-8 60 && \ update-alternatives --config clang -RUN update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-${clangver} 60 && \ +RUN update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-8 60 && \ update-alternatives --config lldb @@ -70,20 +70,16 @@ WORKDIR /usr/local/src/repositories ARG source=https://github.com/FreeRADIUS/freeradius-server.git RUN git clone --depth 1 --no-single-branch ${source} - # -# Install build dependencies for all v3 branches +# Install build dependencies for all branches from v3 onwards # WORKDIR freeradius-server -RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^v3\..*\.x");\ +RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ do \ git checkout $i; \ if [ -e ./debian/control.in ] ; then \ debian/rules debian/control ; \ fi ; \ - echo Installing dependencies for $i ; \ - mk-build-deps debian/control ; \ - apt-get --no-install-recommends -y -V install ./freeradius-build-deps*.deb || true ; \ - apt-get -y -f remove freeradius-build-deps libiodbc2-dev || true ; \ - rm ./freeradius-build-deps*.deb ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ done diff --git a/scripts/crossbuild/docker/ubuntu16/Dockerfile b/scripts/crossbuild/docker/debian11/Dockerfile index dbec6f9..0a77893 100644 --- a/scripts/crossbuild/docker/ubuntu16/Dockerfile +++ b/scripts/crossbuild/docker/debian11/Dockerfile @@ -1,8 +1,10 @@ -FROM ubuntu:16.04 - -ARG gccver=4.9 -ARG clangver=5.0 -ARG osname=xenial +# Auto generated for debian11 +# from scripts/crossbuild/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make crossbuild.debian11.regen` +# +ARG from=debian:bullseye +FROM ${from} as build ARG DEBIAN_FRONTEND=noninteractive @@ -10,24 +12,16 @@ ARG DEBIAN_FRONTEND=noninteractive # Install add-apt-repository # RUN apt-get update && \ - apt-get install -y software-properties-common python-software-properties apt-transport-https curl && \ + apt-get install -y software-properties-common gnupg2 procps && \ apt-get clean && \ rm -r /var/lib/apt/lists/* -# Requires GCC-4.9 as it has support for C11 keywords and atomics - -# For clang -RUN add-apt-repository -y "deb http://apt.llvm.org/${osname}/ llvm-toolchain-${osname}-${clangver} main" && \ - curl -o /tmp/llvm-snapshot.gpg.key https://apt.llvm.org/llvm-snapshot.gpg.key && \ - apt-key add /tmp/llvm-snapshot.gpg.key && \ -# For GCC - add-apt-repository -y ppa:ubuntu-toolchain-r/test RUN apt-get update && \ # Development utilities apt-get install -y devscripts equivs git quilt rsync && \ # Compilers - apt-get install -y g++-${gccver} llvm-${clangver} clang-${clangver} lldb-${clangver} && \ + apt-get install -y g++ llvm clang lldb && \ # eapol_test dependencies apt-get install -y libnl-3-dev libnl-genl-3-dev @@ -38,9 +32,9 @@ RUN apt-get update && \ # - doxygen & JSON.pm RUN apt-get install -y doxygen graphviz libjson-perl # - antora (npm needed) -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - RUN apt-get install -y nodejs -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 # - pandoc WORKDIR /tmp RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) @@ -49,16 +43,6 @@ RUN apt-get install -y ./pandoc-*.deb RUN apt-get install -y ruby-dev RUN gem install asciidoctor -# set default things -RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${gccver} 50 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-${gccver} && \ - update-alternatives --config gcc - -RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${clangver} 60 && \ - update-alternatives --config clang - -RUN update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-${clangver} 60 && \ - update-alternatives --config lldb # @@ -82,5 +66,9 @@ WORKDIR freeradius-server RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ do \ git checkout $i; \ - if [ -e ./debian/control.in ] ; then debian/rules debian/control ; fi ; echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control ; \ + if [ -e ./debian/control.in ] ; then \ + debian/rules debian/control ; \ + fi ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ done diff --git a/scripts/crossbuild/docker/debian12/Dockerfile b/scripts/crossbuild/docker/debian12/Dockerfile new file mode 100644 index 0000000..78eb501 --- /dev/null +++ b/scripts/crossbuild/docker/debian12/Dockerfile @@ -0,0 +1,74 @@ +# Auto generated for debian12 +# from scripts/crossbuild/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make crossbuild.debian12.regen` +# +ARG from=debian:bookworm +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install add-apt-repository +# +RUN apt-get update && \ + apt-get install -y software-properties-common gnupg2 procps && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + + +RUN apt-get update && \ +# Development utilities + apt-get install -y devscripts equivs git quilt rsync && \ +# Compilers + apt-get install -y g++ llvm clang lldb && \ +# eapol_test dependencies + apt-get install -y libnl-3-dev libnl-genl-3-dev + +# +# Documentation build dependecies +# + +# - doxygen & JSON.pm +RUN apt-get install -y doxygen graphviz libjson-perl +# - antora (npm needed) +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - +RUN apt-get install -y nodejs +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 +# - pandoc +WORKDIR /tmp +RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) +RUN apt-get install -y ./pandoc-*.deb +# - asciidoctor +RUN apt-get install -y ruby-dev +RUN gem install asciidoctor + + + +# +# Setup a src dir in /usr/local +# +RUN mkdir -p /usr/local/src/repositories +WORKDIR /usr/local/src/repositories + + +# +# Shallow clone the FreeRADIUS source +# +WORKDIR /usr/local/src/repositories +ARG source=https://github.com/FreeRADIUS/freeradius-server.git +RUN git clone --depth 1 --no-single-branch ${source} + +# +# Install build dependencies for all branches from v3 onwards +# +WORKDIR freeradius-server +RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ + do \ + git checkout $i; \ + if [ -e ./debian/control.in ] ; then \ + debian/rules debian/control ; \ + fi ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ + done diff --git a/scripts/crossbuild/docker/debian8/Dockerfile b/scripts/crossbuild/docker/debian8/Dockerfile deleted file mode 100644 index 094faa3..0000000 --- a/scripts/crossbuild/docker/debian8/Dockerfile +++ /dev/null @@ -1,84 +0,0 @@ -FROM debian:jessie - -ARG gccver=4.9 -ARG clangver=5.0 -ARG osname=jessie - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install add-apt-repository -# -RUN apt-get update && \ - apt-get install -y software-properties-common python-software-properties apt-transport-https curl && \ - apt-get clean && \ - rm -r /var/lib/apt/lists/* - -# Requires GCC-4.9 as it has support for C11 keywords and atomics - -# For clang -RUN add-apt-repository -y "deb http://apt.llvm.org/${osname}/ llvm-toolchain-${osname}-${clangver} main" && \ - curl -o /tmp/llvm-snapshot.gpg.key https://apt.llvm.org/llvm-snapshot.gpg.key && \ - apt-key add /tmp/llvm-snapshot.gpg.key - -RUN apt-get update && \ -# Development utilities - apt-get install -y devscripts equivs git quilt rsync && \ -# Compilers - apt-get install -y g++-${gccver} llvm-${clangver} clang-${clangver} lldb-${clangver} && \ -# eapol_test dependencies - apt-get install -y libnl-3-dev libnl-genl-3-dev - -# -# Documentation build dependecies -# - -# - doxygen & JSON.pm -RUN apt-get install -y doxygen graphviz libjson-perl -# - antora (npm needed) -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - -RUN apt-get install -y nodejs -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 -# - pandoc -WORKDIR /tmp -RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) -RUN dpkg -i ./pandoc-*.deb -RUN apt-get install -fy -# - asciidoctor -RUN apt-get install -y ruby -RUN gem install asciidoctor - -# set default things -RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${gccver} 50 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-${gccver} && \ - update-alternatives --config gcc - -RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${clangver} 60 && \ - update-alternatives --config clang - -RUN update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-${clangver} 60 && \ - update-alternatives --config lldb - - -# -# Setup a src dir in /usr/local -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -RUN git clone --depth 1 --no-single-branch ${source} - -# -# Install build dependencies for all branches from v3 onwards -# -WORKDIR freeradius-server -RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ - do \ - git checkout $i; \ - if [ -e ./debian/control.in ] ; then debian/rules debian/control ; fi ; echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control ; \ - done diff --git a/scripts/crossbuild/docker/debian9/README b/scripts/crossbuild/docker/debian9/README deleted file mode 100644 index f7a6135..0000000 --- a/scripts/crossbuild/docker/debian9/README +++ /dev/null @@ -1,15 +0,0 @@ - -Build source image - - docker build . -f Dockerfile.source -t freeradius:debian9-source - -Then either build and run jenkins image - - docker build . -f Dockerfile.jenkins -t freeradius:debian9-jenkins - docker run -d -p 2222:22 freeradius:debian9-jenkins - -or build and run the server - - docker build . -t freeradius:debian9 - docker run -d -p 1812:1812/udp -p 1813:1813/udp freeradius:debian9 - diff --git a/scripts/crossbuild/docker/centos8/Dockerfile b/scripts/crossbuild/docker/rocky8/Dockerfile index bd856af..ba7fc56 100644 --- a/scripts/crossbuild/docker/centos8/Dockerfile +++ b/scripts/crossbuild/docker/rocky8/Dockerfile @@ -1,27 +1,38 @@ -FROM centos:centos8 +# Auto generated for rocky8 +# from scripts/crossbuild/m4/Dockerfile.rpm.m4 +# +# Rebuild this file with `make crossbuild.rocky8.regen` +# +ARG from=rockylinux/rockylinux:8 +FROM ${from} as build # # Install devtools like make and git and the EPEL # repository for freetds and hiredis # RUN yum update -y -RUN yum install -y rpmdevtools openssl epel-release git yum-utils rsync dnf-plugins-core -RUN dnf config-manager --set-enabled powertools +RUN yum install -y rpmdevtools openssl epel-release git procps yum-utils \ + rsync dnf-plugins-core + + +RUN yum config-manager --set-enabled powertools # # Install GCC that has the requisite support for C11 keywords and atomics # RUN yum install -y gcc-toolset-9 + # # Documentation build dependecies # + # - doxygen & JSON.pm RUN yum install -y doxygen graphviz perl-JSON # - antora (npm needed) -RUN curl -sL https://rpm.nodesource.com/setup_10.x | bash - +RUN curl -sL https://rpm.nodesource.com/setup_20.x | bash - RUN yum install -y nodejs -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 # - pandoc RUN curl -o - -L $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*tar.gz" | cut -d '"' -f 4) | tar xzvf - -C /tmp/ RUN mv /tmp/pandoc-*/bin/* /usr/local/bin @@ -56,14 +67,19 @@ RUN git clone --depth 1 --no-single-branch ${source} # # Install build dependencies for all branches from v3 onwards +# Nodesource has issues (no SRPMS in some repos) and is not needed here +# CentOS/RHEL 7 do not support "-D" for yum-builddep so do that separately below if needed # WORKDIR freeradius-server RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ do \ git checkout $i; \ - [ -e redhat/freeradius.spec ] && yum-builddep -y redhat/freeradius.spec; \ + [ -e redhat/freeradius.spec ] && yum-builddep -D "_with_rlm_yubikey 1" -y redhat/freeradius.spec; \ done + +RUN yum install -y libyubikey-devel + # # Which is required by fixture setup utilities # @@ -75,7 +91,12 @@ RUN yum install -y which RUN yum install -y libnl3-devel # +# We test with TLS1.1, but that is disabled by default on some +# newer systems. +# +RUN update-crypto-policies --set LEGACY + +# # Create the RPM build tree # -ENV BUILDDIR=/root/rpmbuild RUN rpmdev-setuptree diff --git a/scripts/crossbuild/docker/rocky9/Dockerfile b/scripts/crossbuild/docker/rocky9/Dockerfile new file mode 100644 index 0000000..9936fe4 --- /dev/null +++ b/scripts/crossbuild/docker/rocky9/Dockerfile @@ -0,0 +1,91 @@ +# Auto generated for rocky9 +# from scripts/crossbuild/m4/Dockerfile.rpm.m4 +# +# Rebuild this file with `make crossbuild.rocky9.regen` +# +ARG from=rockylinux/rockylinux:9 +FROM ${from} as build + +# +# Install yum +# +RUN dnf install -y yum + +# +# Install devtools like make and git and the EPEL +# repository for freetds and hiredis +# +RUN yum update -y +RUN yum install -y rpmdevtools openssl epel-release git procps yum-utils \ + rsync dnf-plugins-core + + + +RUN yum config-manager --set-enabled crb + +# +# Documentation build dependecies +# + +# - doxygen & JSON.pm +RUN yum install -y doxygen graphviz perl-JSON +# - antora (npm needed) +RUN curl -sL https://rpm.nodesource.com/setup_20.x | bash - +RUN yum install -y nodejs +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 +# - pandoc +RUN curl -o - -L $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*tar.gz" | cut -d '"' -f 4) | tar xzvf - -C /tmp/ +RUN mv /tmp/pandoc-*/bin/* /usr/local/bin +# - asciidoctor +RUN yum install -y rubygems-devel +RUN gem install asciidoctor + +# +# Setup a src dir in /usr/local +# +RUN mkdir -p /usr/local/src/repositories +WORKDIR /usr/local/src/repositories + + +# +# Shallow clone the FreeRADIUS source +# +WORKDIR /usr/local/src/repositories +ARG source=https://github.com/FreeRADIUS/freeradius-server.git +RUN git clone --depth 1 --no-single-branch ${source} + +# +# Install build dependencies for all branches from v3 onwards +# Nodesource has issues (no SRPMS in some repos) and is not needed here +# CentOS/RHEL 7 do not support "-D" for yum-builddep so do that separately below if needed +# +WORKDIR freeradius-server +RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ + do \ + git checkout $i; \ + [ -e redhat/freeradius.spec ] && yum-builddep -D "_with_rlm_yubikey 1" -y redhat/freeradius.spec; \ + done + + +RUN yum install -y libyubikey-devel + +# +# Which is required by fixture setup utilities +# +RUN yum install -y which + +# +# Explicitly install libnl3-devel which is required for the EAP tests +# +RUN yum install -y libnl3-devel + +# +# We test with TLS1.1, but that is disabled by default on some +# newer systems. +# +RUN update-crypto-policies --set LEGACY + +# +# Create the RPM build tree +# +RUN rpmdev-setuptree diff --git a/scripts/crossbuild/docker/ubuntu18/Dockerfile b/scripts/crossbuild/docker/ubuntu18/Dockerfile index 874e3ec..1bea0bf 100644 --- a/scripts/crossbuild/docker/ubuntu18/Dockerfile +++ b/scripts/crossbuild/docker/ubuntu18/Dockerfile @@ -1,8 +1,10 @@ -FROM ubuntu:18.04 - -ARG gccver=4.9 -ARG clangver=5.0 -ARG osname=bionic +# Auto generated for ubuntu18 +# from scripts/crossbuild/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make crossbuild.ubuntu18.regen` +# +ARG from=ubuntu:18.04 +FROM ${from} as build ARG DEBIAN_FRONTEND=noninteractive @@ -10,10 +12,11 @@ ARG DEBIAN_FRONTEND=noninteractive # Install add-apt-repository # RUN apt-get update && \ - apt-get install -y software-properties-common && \ + apt-get install -y software-properties-common gnupg2 procps && \ apt-get clean && \ rm -r /var/lib/apt/lists/* + RUN apt-get update && \ # Development utilities apt-get install -y devscripts equivs git quilt rsync && \ @@ -29,9 +32,9 @@ RUN apt-get update && \ # - doxygen & JSON.pm RUN apt-get install -y doxygen graphviz libjson-perl # - antora (npm needed) -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - +RUN curl -sL https://deb.nodesource.com/setup_16.x | bash - RUN apt-get install -y nodejs -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 # - pandoc WORKDIR /tmp RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) @@ -40,6 +43,8 @@ RUN apt-get install -y ./pandoc-*.deb RUN apt-get install -y ruby-dev RUN gem install asciidoctor + + # # Setup a src dir in /usr/local # @@ -61,5 +66,9 @@ WORKDIR freeradius-server RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ do \ git checkout $i; \ - if [ -e ./debian/control.in ] ; then debian/rules debian/control ; fi ; echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control ; \ + if [ -e ./debian/control.in ] ; then \ + debian/rules debian/control ; \ + fi ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ done diff --git a/scripts/crossbuild/docker/ubuntu20/Dockerfile b/scripts/crossbuild/docker/ubuntu20/Dockerfile index c813b2f..a58e634 100644 --- a/scripts/crossbuild/docker/ubuntu20/Dockerfile +++ b/scripts/crossbuild/docker/ubuntu20/Dockerfile @@ -1,8 +1,10 @@ -FROM ubuntu:20.04 - -ARG gccver=4.9 -ARG clangver=5.0 -ARG osname=bionic +# Auto generated for ubuntu20 +# from scripts/crossbuild/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make crossbuild.ubuntu20.regen` +# +ARG from=ubuntu:20.04 +FROM ${from} as build ARG DEBIAN_FRONTEND=noninteractive @@ -10,10 +12,11 @@ ARG DEBIAN_FRONTEND=noninteractive # Install add-apt-repository # RUN apt-get update && \ - apt-get install -y software-properties-common && \ + apt-get install -y software-properties-common gnupg2 procps && \ apt-get clean && \ rm -r /var/lib/apt/lists/* + RUN apt-get update && \ # Development utilities apt-get install -y devscripts equivs git quilt rsync && \ @@ -29,9 +32,9 @@ RUN apt-get update && \ # - doxygen & JSON.pm RUN apt-get install -y doxygen graphviz libjson-perl # - antora (npm needed) -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - RUN apt-get install -y nodejs -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 # - pandoc WORKDIR /tmp RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) @@ -40,6 +43,8 @@ RUN apt-get install -y ./pandoc-*.deb RUN apt-get install -y ruby-dev RUN gem install asciidoctor + + # # Setup a src dir in /usr/local # @@ -61,5 +66,9 @@ WORKDIR freeradius-server RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ do \ git checkout $i; \ - if [ -e ./debian/control.in ] ; then debian/rules debian/control ; fi ; echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control ; \ + if [ -e ./debian/control.in ] ; then \ + debian/rules debian/control ; \ + fi ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ done diff --git a/scripts/crossbuild/docker/ubuntu22/Dockerfile b/scripts/crossbuild/docker/ubuntu22/Dockerfile new file mode 100644 index 0000000..677c912 --- /dev/null +++ b/scripts/crossbuild/docker/ubuntu22/Dockerfile @@ -0,0 +1,74 @@ +# Auto generated for ubuntu22 +# from scripts/crossbuild/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make crossbuild.ubuntu22.regen` +# +ARG from=ubuntu:22.04 +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install add-apt-repository +# +RUN apt-get update && \ + apt-get install -y software-properties-common gnupg2 procps && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + + +RUN apt-get update && \ +# Development utilities + apt-get install -y devscripts equivs git quilt rsync && \ +# Compilers + apt-get install -y g++ llvm clang lldb && \ +# eapol_test dependencies + apt-get install -y libnl-3-dev libnl-genl-3-dev + +# +# Documentation build dependecies +# + +# - doxygen & JSON.pm +RUN apt-get install -y doxygen graphviz libjson-perl +# - antora (npm needed) +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - +RUN apt-get install -y nodejs +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 +# - pandoc +WORKDIR /tmp +RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) +RUN apt-get install -y ./pandoc-*.deb +# - asciidoctor +RUN apt-get install -y ruby-dev +RUN gem install asciidoctor + + + +# +# Setup a src dir in /usr/local +# +RUN mkdir -p /usr/local/src/repositories +WORKDIR /usr/local/src/repositories + + +# +# Shallow clone the FreeRADIUS source +# +WORKDIR /usr/local/src/repositories +ARG source=https://github.com/FreeRADIUS/freeradius-server.git +RUN git clone --depth 1 --no-single-branch ${source} + +# +# Install build dependencies for all branches from v3 onwards +# +WORKDIR freeradius-server +RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ + do \ + git checkout $i; \ + if [ -e ./debian/control.in ] ; then \ + debian/rules debian/control ; \ + fi ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ + done diff --git a/scripts/crossbuild/docker/ubuntu24/Dockerfile b/scripts/crossbuild/docker/ubuntu24/Dockerfile new file mode 100644 index 0000000..966faf0 --- /dev/null +++ b/scripts/crossbuild/docker/ubuntu24/Dockerfile @@ -0,0 +1,74 @@ +# Auto generated for ubuntu24 +# from scripts/crossbuild/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make crossbuild.ubuntu24.regen` +# +ARG from=ubuntu:24.04 +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install add-apt-repository +# +RUN apt-get update && \ + apt-get install -y software-properties-common gnupg2 procps && \ + apt-get clean && \ + rm -r /var/lib/apt/lists/* + + +RUN apt-get update && \ +# Development utilities + apt-get install -y devscripts equivs git quilt rsync fakeroot && \ +# Compilers + apt-get install -y g++ llvm clang lldb && \ +# eapol_test dependencies + apt-get install -y libnl-3-dev libnl-genl-3-dev + +# +# Documentation build dependecies +# + +# - doxygen & JSON.pm +RUN apt-get install -y doxygen graphviz libjson-perl +# - antora (npm needed) +RUN curl -sL https://deb.nodesource.com/setup_20.x | bash - +RUN apt-get install -y nodejs +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 +# - pandoc +WORKDIR /tmp +RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) +RUN apt-get install -y ./pandoc-*.deb +# - asciidoctor +RUN apt-get install -y ruby-dev +RUN gem install asciidoctor + + + +# +# Setup a src dir in /usr/local +# +RUN mkdir -p /usr/local/src/repositories +WORKDIR /usr/local/src/repositories + + +# +# Shallow clone the FreeRADIUS source +# +WORKDIR /usr/local/src/repositories +ARG source=https://github.com/FreeRADIUS/freeradius-server.git +RUN git clone --depth 1 --no-single-branch ${source} + +# +# Install build dependencies for all branches from v3 onwards +# +WORKDIR freeradius-server +RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ + do \ + git checkout $i; \ + if [ -e ./debian/control.in ] ; then \ + debian/rules debian/control ; \ + fi ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ + done diff --git a/scripts/crossbuild/docker/debian9/Dockerfile b/scripts/crossbuild/m4/Dockerfile.deb.m4 index 9b47832..a1bfca8 100644 --- a/scripts/crossbuild/docker/debian9/Dockerfile +++ b/scripts/crossbuild/m4/Dockerfile.deb.m4 @@ -1,8 +1,5 @@ -FROM debian:stretch - -ARG gccver=6 -ARG clangver=5.0 -ARG osname=stretch +ARG from=DOCKER_IMAGE +FROM ${from} as build ARG DEBIAN_FRONTEND=noninteractive @@ -10,35 +7,38 @@ ARG DEBIAN_FRONTEND=noninteractive # Install add-apt-repository # RUN apt-get update && \ - apt-get install -y software-properties-common gnupg2 apt-transport-https curl && \ + apt-get install -y software-properties-common gnupg2 procps && \ apt-get clean && \ rm -r /var/lib/apt/lists/* -# Stretch uses GCC-6.3 by default, so it doesn't need to be updated to get C11 functionality. - +define(`CLANG_PKGS', `llvm clang lldb')dnl +ifelse(D_NAME, `debian10', `dnl +define(`CLANG_VER', `8')dnl +define(`CLANG_PKGS', `llvm-CLANG_VER clang-CLANG_VER lldb-CLANG_VER')dnl # For clang -RUN add-apt-repository -y "deb http://apt.llvm.org/${osname}/ llvm-toolchain-${osname}-${clangver} main" && \ - curl -o /tmp/llvm-snapshot.gpg.key https://apt.llvm.org/llvm-snapshot.gpg.key && \ - apt-key add /tmp/llvm-snapshot.gpg.key +RUN add-apt-repository -y "deb http://apt.llvm.org/OS_CODENAME/ llvm-toolchain-OS_CODENAME-CLANG_VER main" && \ + apt-key adv --fetch-keys http://apt.llvm.org/llvm-snapshot.gpg.key +')dnl RUN apt-get update && \ # Development utilities - apt-get install -y devscripts equivs git quilt rsync && \ + apt-get install -y devscripts equivs git quilt rsync fakeroot && \ # Compilers - apt-get install -y g++-${gccver} llvm-${clangver} clang-${clangver} lldb-${clangver} && \ + apt-get install -y g++ CLANG_PKGS && \ # eapol_test dependencies apt-get install -y libnl-3-dev libnl-genl-3-dev # # Documentation build dependecies # +define(`NODE_VER', ifelse(D_NAME, `ubuntu18', `16', `20'))dnl # - doxygen & JSON.pm RUN apt-get install -y doxygen graphviz libjson-perl # - antora (npm needed) -RUN curl -sL https://deb.nodesource.com/setup_10.x | bash - -RUN apt-get install -y npm -RUN npm i -g @antora/cli@2.1 @antora/site-generator-default@2.1 +RUN curl -sL https://deb.nodesource.com/setup_`'NODE_VER.x | bash - +RUN apt-get install -y nodejs +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 # - pandoc WORKDIR /tmp RUN curl -OL $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*deb" | cut -d '"' -f 4) @@ -47,17 +47,16 @@ RUN apt-get install -y ./pandoc-*.deb RUN apt-get install -y ruby-dev RUN gem install asciidoctor -# set default things -RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-${gccver} 50 \ - --slave /usr/bin/g++ g++ /usr/bin/g++-${gccver} && \ - update-alternatives --config gcc - -RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-${clangver} 60 && \ +ifelse(D_NAME, `debian10', `dnl +# +# Set defaults +# +RUN update-alternatives --install /usr/bin/clang clang /usr/bin/clang-CLANG_VER 60 && \ update-alternatives --config clang -RUN update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-${clangver} 60 && \ +RUN update-alternatives --install /usr/bin/lldb lldb /usr/bin/lldb-CLANG_VER 60 && \ update-alternatives --config lldb - +') # # Setup a src dir in /usr/local @@ -80,5 +79,9 @@ WORKDIR freeradius-server RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ do \ git checkout $i; \ - if [ -e ./debian/control.in ] ; then debian/rules debian/control ; fi ; echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control ; \ + if [ -e ./debian/control.in ] ; then \ + debian/rules debian/control ; \ + fi ; \ + echo 'y' | \ + mk-build-deps -irt'apt-get -yV' debian/control ; \ done diff --git a/scripts/crossbuild/m4/Dockerfile.rpm.m4 b/scripts/crossbuild/m4/Dockerfile.rpm.m4 new file mode 100644 index 0000000..714ee6e --- /dev/null +++ b/scripts/crossbuild/m4/Dockerfile.rpm.m4 @@ -0,0 +1,146 @@ +ARG from=DOCKER_IMAGE +FROM ${from} as build + +ifelse(OS_VER, 7, `dnl +# +# CentOS 7 is now EOL, so we need to fix up the repo source +# +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* +')dnl + +ifelse(OS_VER, `9', `dnl +# +# Install yum +# +RUN dnf install -y yum +')dnl + +# +# Install devtools like make and git and the EPEL +# repository for freetds and hiredis +# +RUN yum update -y +RUN yum install -y rpmdevtools openssl epel-release git procps yum-utils \ + rsync ifelse(OS_VER, `7',, `dnf-plugins-core') + +ifelse(OS_VER, `7', `dnl +# +# Install GCC that has the requisite support for C11 keywords and atomics +# +RUN yum install -y centos-release-scl +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* +RUN yum install -y devtoolset-8-gcc devtoolset-8-gcc-c++ +ENV CC=/opt/rh/devtoolset-8/root/usr/bin/gcc + +# +# Remove the CentOS-SCLo repo which is apparently not valid? +# See: https://bugs.centos.org/view.php?id=14773 +# +RUN rm /etc/yum.repos.d/CentOS-SCLo-scl-rh.repo +RUN rm /etc/yum.repos.d/CentOS-SCLo-scl.repo +')dnl + +ifelse(OS_VER, `8', `dnl +RUN yum config-manager --set-enabled powertools + +# +# Install GCC that has the requisite support for C11 keywords and atomics +# +RUN yum install -y gcc-toolset-9 +')dnl + +ifelse(OS_VER, `9', `dnl +RUN yum config-manager --set-enabled crb +')dnl + +# +# Documentation build dependecies +# +define(`NODE_VER', ifelse(OS_VER, 7, `16', `20'))dnl + +# - doxygen & JSON.pm +RUN yum install -y doxygen graphviz perl-JSON +# - antora (npm needed) +RUN curl -sL https://rpm.nodesource.com/setup_`'NODE_VER.x | bash - +RUN yum install -y nodejs +RUN npm i -g @antora/cli@3.1.7 @antora/site-generator-default@3.1.7 +# - pandoc +RUN curl -o - -L $(curl -s https://api.github.com/repos/jgm/pandoc/releases/latest | grep "browser_download_url.*tar.gz" | cut -d '"' -f 4) | tar xzvf - -C /tmp/ +RUN mv /tmp/pandoc-*/bin/* /usr/local/bin +# - asciidoctor +RUN yum install -y rubygems-devel +RUN gem install asciidoctor + +# +# Setup a src dir in /usr/local +# +RUN mkdir -p /usr/local/src/repositories +WORKDIR /usr/local/src/repositories + +changequote([{,}])dnl Only add LTB on centos7/rocky8 +ifelse(ifelse(OS_VER, 7, yes, OS_VER, 8, yes, no), yes, [{dnl +# +# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS +# +RUN echo $'[ltb-project]\n\ +name=LTB project packages\n\ +baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ +enabled=1\n\ +gpgcheck=1\n\ +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ +> /etc/yum.repos.d/ltb-project.repo +RUN rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project +}])dnl +changequote(`,')dnl + +# +# Shallow clone the FreeRADIUS source +# +WORKDIR /usr/local/src/repositories +ARG source=https://github.com/FreeRADIUS/freeradius-server.git +RUN git clone --depth 1 --no-single-branch ${source} + +# +# Install build dependencies for all branches from v3 onwards +# Nodesource has issues (no SRPMS in some repos) and is not needed here +# CentOS/RHEL 7 do not support "-D" for yum-builddep so do that separately below if needed +# +define(`BUILDDEP_EXTRA', ifelse(OS_VER, 7, `--disablerepo="nodesource*"', `-D "_with_rlm_yubikey 1"'))dnl +WORKDIR freeradius-server +RUN for i in $(git for-each-ref --format='%(refname:short)' refs/remotes/origin 2>/dev/null | sed -e 's#origin/##' | egrep "^(v[3-9]*\.[0-9x]*\.x|master)$");\ + do \ + git checkout $i; \ + [ -e redhat/freeradius.spec ] && yum-builddep BUILDDEP_EXTRA -y redhat/freeradius.spec; \ + done + +ifelse(OS_VER, 7,`dnl +# Yubikey deps for CentOS/RHEL 7 +RUN yum install -y ykclient-devel ykclient +')dnl + +RUN yum install -y libyubikey-devel + +# +# Which is required by fixture setup utilities +# +RUN yum install -y which + +# +# Explicitly install libnl3-devel which is required for the EAP tests +# +RUN yum install -y libnl3-devel + +ifelse(OS_VER, 7,, `dnl +# +# We test with TLS1.1, but that is disabled by default on some +# newer systems. +# +RUN update-crypto-policies --set LEGACY +')dnl + +# +# Create the RPM build tree +# +RUN rpmdev-setuptree diff --git a/scripts/docker/centos7/Dockerfile b/scripts/docker/centos7/Dockerfile deleted file mode 100644 index efa56eb..0000000 --- a/scripts/docker/centos7/Dockerfile +++ /dev/null @@ -1,96 +0,0 @@ -ARG from=centos:centos7 -FROM ${from} as build - -# -# Install build tools -# -RUN yum groupinstall -y "Development Tools" -RUN yum install -y rpmdevtools -RUN yum install -y openssl - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Other requirements -# - -# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS -RUN echo $'[ltb-project]\n\ -name=LTB project packages\n\ -baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ -enabled=1\n\ -gpgcheck=1\n\ -gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ -> /etc/yum.repos.d/ltb-project.repo -RUN rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project - -# EPEL repository for freetds and hiredis -RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm - -# -# Install build dependencies -# -RUN [ -e redhat/freeradius.spec ] && yum-builddep -y redhat/freeradius.spec - -# -# Create RPM build environment -# -ENV BUILDDIR=/root/rpmbuild -RUN rpmdev-setuptree - -RUN ./configure -RUN make freeradius-server-$(cat VERSION).tar.bz2 -RUN cp freeradius-server-$(cat VERSION).tar.bz2 $BUILDDIR/SOURCES/ -RUN cp -r redhat/* $BUILDDIR/SOURCES/ -RUN cp -r redhat/freeradius.spec $BUILDDIR/SPECS/ -WORKDIR $BUILDDIR - -# -# Build the server -# -ENV QA_RPATHS=0x0003 -RUN rpmbuild -bb --define '_release $release' "$BUILDDIR/SPECS/freeradius.spec" - -RUN mkdir /root/rpms -RUN mv $BUILDDIR/RPMS/*/*.rpm /root/rpms/ - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /root/rpms /tmp/ - -# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS -RUN echo $'[ltb-project]\n\ -name=LTB project packages\n\ -baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ -enabled=1\n\ -gpgcheck=1\n\ -gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ -> /etc/yum.repos.d/ltb-project.repo \ - && rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project \ - \ -# EPEL repository for freetds and hiredis - && yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \ - \ - && yum install -y /tmp/*.rpm - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["radiusd"] diff --git a/scripts/docker/debian10/Dockerfile b/scripts/docker/debian10/Dockerfile deleted file mode 100644 index 441bed7..0000000 --- a/scripts/docker/debian10/Dockerfile +++ /dev/null @@ -1,59 +0,0 @@ -ARG from=debian:buster -FROM ${from} as build - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install build tools -# -RUN apt-get update -RUN apt-get install -y devscripts equivs git quilt gcc - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Install build dependencies -# -RUN git checkout ${release}; \ - if [ -e ./debian/control.in ]; then \ - debian/rules debian/control; \ - fi; \ - echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control - -# -# Build the server -# -RUN make -j2 deb - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /usr/local/src/repositories/*.deb /tmp/ - -RUN apt-get update \ - && apt-get install -y /tmp/*.deb \ - && apt-get clean \ - && rm -r /var/lib/apt/lists/* /tmp/*.deb \ - \ - && ln -s /etc/freeradius /etc/raddb - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["freeradius"] diff --git a/scripts/docker/debian11/Dockerfile b/scripts/docker/debian11/Dockerfile deleted file mode 100644 index 7a9931c..0000000 --- a/scripts/docker/debian11/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ -ARG from=debian:bullseye -FROM ${from} as build - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install build tools -# -RUN apt-get update -RUN apt-get install -y devscripts equivs git quilt gcc - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Install build dependencies -# -RUN git checkout ${release}; \ - if [ -e ./debian/control.in ]; then \ - debian/rules debian/control; \ - fi; \ - echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control - -# -# Build the server -# -RUN make -j2 deb - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /usr/local/src/repositories/*.deb /tmp/ - -ARG freerad_uid=101 -ARG freerad_gid=101 - -RUN groupadd -g ${freerad_gid} -r freerad \ - && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ - && apt-get update \ - && apt-get install -y /tmp/*.deb \ - && apt-get clean \ - && rm -r /var/lib/apt/lists/* /tmp/*.deb \ - \ - && ln -s /etc/freeradius /etc/raddb - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["freeradius"] diff --git a/scripts/docker/debian9/Dockerfile b/scripts/docker/debian9/Dockerfile deleted file mode 100644 index 1a34f7f..0000000 --- a/scripts/docker/debian9/Dockerfile +++ /dev/null @@ -1,59 +0,0 @@ -ARG from=debian:stretch -FROM ${from} as build - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install build tools -# -RUN apt-get update -RUN apt-get install -y devscripts equivs git quilt gcc - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Install build dependencies -# -RUN git checkout ${release}; \ - if [ -e ./debian/control.in ]; then \ - debian/rules debian/control; \ - fi; \ - echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control - -# -# Build the server -# -RUN make -j2 deb - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /usr/local/src/repositories/*.deb /tmp/ - -RUN apt-get update \ - && apt-get install -y /tmp/*.deb \ - && apt-get clean \ - && rm -r /var/lib/apt/lists/* /tmp/*.deb \ - \ - && ln -s /etc/freeradius /etc/raddb - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["freeradius"] diff --git a/scripts/docker/debiansid/Dockerfile b/scripts/docker/debiansid/Dockerfile deleted file mode 100644 index 191ec49..0000000 --- a/scripts/docker/debiansid/Dockerfile +++ /dev/null @@ -1,64 +0,0 @@ -ARG from=debian:sid -FROM ${from} as build - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install build tools -# -RUN apt-get update -RUN apt-get install -y devscripts equivs git quilt gcc - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Install build dependencies -# -RUN git checkout ${release}; \ - if [ -e ./debian/control.in ]; then \ - debian/rules debian/control; \ - fi; \ - echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control - -# -# Build the server -# -RUN make -j2 deb - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /usr/local/src/repositories/*.deb /tmp/ - -ARG freerad_uid=101 -ARG freerad_gid=101 - -RUN groupadd -g ${freerad_gid} -r freerad \ - && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ - && apt-get update \ - && apt-get install -y /tmp/*.deb \ - && apt-get clean \ - && rm -r /var/lib/apt/lists/* /tmp/*.deb \ - \ - && ln -s /etc/freeradius /etc/raddb - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["freeradius"] diff --git a/scripts/docker/dists/alpine/Dockerfile b/scripts/docker/dists/alpine/Dockerfile new file mode 100644 index 0000000..04c0101 --- /dev/null +++ b/scripts/docker/dists/alpine/Dockerfile @@ -0,0 +1,95 @@ +# Auto generated for alpine +# from scripts/docker/m4/Dockerfile.alpine.m4 +# +# Rebuild this file with `make docker.alpine.regen` +# +ARG from=alpine:3.13 +FROM ${from} as build + +# +# Install build tools +# +RUN apk update +RUN apk add git gcc make + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +RUN [ -z "$release" ] || git checkout ${release} + + +# +# Install build dependencies +# +# essential +RUN apk add libc-dev talloc-dev +RUN apk add openssl openssl-dev +RUN apk add linux-headers +# general +RUN apk add pcre-dev libidn-dev krb5-dev samba-dev curl-dev json-c-dev +RUN apk add openldap-dev unbound-dev +# languages +RUN apk add ruby-dev perl-dev python2-dev python3-dev +# databases +RUN apk add hiredis-dev libmemcached-dev gdbm-dev libcouchbase-dev +# sql +RUN apk add postgresql-dev mariadb-dev unixodbc-dev sqlite-dev + +# +# Build the server +# +RUN ./configure --prefix=/opt +RUN make -j2 +RUN make install +RUN rm /opt/lib/*.a + +# +# Clean environment and run the server +# +FROM ${from} +COPY --from=build /opt /opt + +# +# These are needed for the server to start +# +RUN apk update \ + && apk add talloc libressl pcre libwbclient tzdata \ + \ +# +# Libraries that are needed dependent on which modules are used +# Some of these (especially the languages) are huge. A reasonable +# selection has been enabled here. If you use modules needing +# other dependencies then install any others required in your +# local Dockerfile. +# + && apk add libcurl json-c libldap hiredis sqlite-dev \ +#RUN apk add libidn krb5 +#RUN apk add unbound-libs +#RUN apk add ruby-libs perl python2-dev python3-dev +#RUN apk add libmemcached gdbm libcouchbase +#RUN apk add postgresql-dev mariadb-dev unixodbc-dev + \ + && ln -s /opt/etc/raddb /etc/raddb + +WORKDIR / +COPY scripts/docker//etc/docker-entrypoint.sh.alpine docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["radiusd"] diff --git a/scripts/docker/alpine/docker-entrypoint.sh b/scripts/docker/dists/alpine/docker-entrypoint.sh index e0f9f6f..e0f9f6f 100755 --- a/scripts/docker/alpine/docker-entrypoint.sh +++ b/scripts/docker/dists/alpine/docker-entrypoint.sh diff --git a/scripts/docker/dists/centos7/Dockerfile b/scripts/docker/dists/centos7/Dockerfile new file mode 100644 index 0000000..0e266b7 --- /dev/null +++ b/scripts/docker/dists/centos7/Dockerfile @@ -0,0 +1,134 @@ +# Auto generated for centos7 +# from scripts/docker/m4/Dockerfile.rpm.m4 +# +# Rebuild this file with `make docker.centos7.regen` +# +ARG from=centos:7 +FROM ${from} as build + +# +# CentOS 7 is now EOL, so we need to fix up the repo source +# +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* + + + +# +# Install build tools +# +RUN yum groupinstall -y "Development Tools" +RUN yum install -y rpmdevtools +RUN yum install -y openssl + + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Other requirements +# + +# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS +RUN echo $'[ltb-project]\n\ +name=LTB project packages\n\ +baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ +enabled=1\n\ +gpgcheck=1\n\ +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ +> /etc/yum.repos.d/ltb-project.repo +RUN rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project + +# Enable EPEL repository for freetds and hiredis +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm + +# +# Install build dependencies +# +# Run twice, it doesn't always get everything with one invocation +# +RUN [ -e redhat/freeradius.spec ] && \ + yum-builddep -y redhat/freeradius.spec && \ + yum-builddep -y redhat/freeradius.spec + +# +# Create RPM build environment +# +ENV BUILDDIR=/root/rpmbuild +RUN rpmdev-setuptree + +RUN ./configure +RUN cp VERSION /VERSION +RUN make freeradius-server-$(cat /VERSION).tar.bz2 +RUN cp freeradius-server-$(cat /VERSION).tar.bz2 $BUILDDIR/SOURCES/ +RUN cp -r redhat/* $BUILDDIR/SOURCES/ +RUN sed -i "s/^Version:.*/Version: $(cat /VERSION)/" redhat/freeradius.spec +RUN cp -r redhat/freeradius.spec $BUILDDIR/SPECS/ +WORKDIR $BUILDDIR + +# +# Build the server +# +ENV QA_RPATHS=0x0003 +RUN rpmbuild -bb --define "_release $(cat /VERSION)" "$BUILDDIR/SPECS/freeradius.spec" + +RUN mkdir /root/rpms +RUN mv $BUILDDIR/RPMS/*/*.rpm /root/rpms/ + +# +# Clean environment and run the server +# +FROM ${from} + +COPY --from=build /root/rpms /tmp/ + +# +# CentOS 7 is now EOL, so we need to fix up the repo source +# +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* + + +# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS +RUN echo $'[ltb-project]\n\ +name=LTB project packages\n\ +baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ +enabled=1\n\ +gpgcheck=1\n\ +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ +> /etc/yum.repos.d/ltb-project.repo \ + && rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project + + +# EPEL repository for freetds and hiredis +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm \ + \ + && yum install -y /tmp/*.rpm + +WORKDIR / +COPY scripts/docker//etc/docker-entrypoint.sh.rpm docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["radiusd"] diff --git a/scripts/docker/centos7/docker-entrypoint.sh b/scripts/docker/dists/centos7/docker-entrypoint.sh index 900ad6b..900ad6b 100755 --- a/scripts/docker/centos7/docker-entrypoint.sh +++ b/scripts/docker/dists/centos7/docker-entrypoint.sh diff --git a/scripts/docker/dists/debian10/Dockerfile b/scripts/docker/dists/debian10/Dockerfile new file mode 100644 index 0000000..5e9e9a5 --- /dev/null +++ b/scripts/docker/dists/debian10/Dockerfile @@ -0,0 +1,82 @@ +# Auto generated for debian10 +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.debian10.regen` +# +ARG from=debian:buster +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +RUN apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/debian10/docker-entrypoint.sh b/scripts/docker/dists/debian10/docker-entrypoint.sh index 93141b0..93141b0 100755 --- a/scripts/docker/debian10/docker-entrypoint.sh +++ b/scripts/docker/dists/debian10/docker-entrypoint.sh diff --git a/scripts/docker/dists/debian11/Dockerfile b/scripts/docker/dists/debian11/Dockerfile new file mode 100644 index 0000000..f709d95 --- /dev/null +++ b/scripts/docker/dists/debian11/Dockerfile @@ -0,0 +1,87 @@ +# Auto generated for debian11 +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.debian11.regen` +# +ARG from=debian:bullseye +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +ARG freerad_uid=101 +ARG freerad_gid=101 + +RUN groupadd -g ${freerad_gid} -r freerad \ + && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ + && apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/debian11/docker-entrypoint.sh b/scripts/docker/dists/debian11/docker-entrypoint.sh index 93141b0..93141b0 100755 --- a/scripts/docker/debian11/docker-entrypoint.sh +++ b/scripts/docker/dists/debian11/docker-entrypoint.sh diff --git a/scripts/docker/dists/debian12/Dockerfile b/scripts/docker/dists/debian12/Dockerfile new file mode 100644 index 0000000..609e8ae --- /dev/null +++ b/scripts/docker/dists/debian12/Dockerfile @@ -0,0 +1,87 @@ +# Auto generated for debian12 +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.debian12.regen` +# +ARG from=debian:bookworm +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +ARG freerad_uid=101 +ARG freerad_gid=101 + +RUN groupadd -g ${freerad_gid} -r freerad \ + && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ + && apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/debian9/docker-entrypoint.sh b/scripts/docker/dists/debian12/docker-entrypoint.sh index 93141b0..93141b0 100755 --- a/scripts/docker/debian9/docker-entrypoint.sh +++ b/scripts/docker/dists/debian12/docker-entrypoint.sh diff --git a/scripts/docker/dists/debiansid/Dockerfile b/scripts/docker/dists/debiansid/Dockerfile new file mode 100644 index 0000000..862a421 --- /dev/null +++ b/scripts/docker/dists/debiansid/Dockerfile @@ -0,0 +1,87 @@ +# Auto generated for debiansid +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.debiansid.regen` +# +ARG from=debian:sid +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +ARG freerad_uid=101 +ARG freerad_gid=101 + +RUN groupadd -g ${freerad_gid} -r freerad \ + && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ + && apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/debiansid/docker-entrypoint.sh b/scripts/docker/dists/debiansid/docker-entrypoint.sh index 93141b0..93141b0 100755 --- a/scripts/docker/debiansid/docker-entrypoint.sh +++ b/scripts/docker/dists/debiansid/docker-entrypoint.sh diff --git a/scripts/docker/rocky8/Dockerfile b/scripts/docker/dists/rocky8/Dockerfile index ca821a3..4dcb92e 100644 --- a/scripts/docker/rocky8/Dockerfile +++ b/scripts/docker/dists/rocky8/Dockerfile @@ -1,34 +1,54 @@ +# Auto generated for rocky8 +# from scripts/docker/m4/Dockerfile.rpm.m4 +# +# Rebuild this file with `make docker.rocky8.regen` +# ARG from=rockylinux/rockylinux:8 FROM ${from} as build +# +# Install yum +# +RUN dnf install -y yum + RUN rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial # # Install build tools # RUN yum groupinstall -y "Development Tools" + RUN yum install -y rpmdevtools openssl dnf-utils + # # Create build directory # -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . # -# Shallow clone the FreeRADIUS source +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system # -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x +RUN git clean -fdxx \ + && git reset --hard HEAD -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline # # Other requirements # -# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS +# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS RUN echo $'[ltb-project]\n\ name=LTB project packages\n\ baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ @@ -38,17 +58,23 @@ gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ > /etc/yum.repos.d/ltb-project.repo RUN rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project -# EPEL repository for freetds and hiredis +# Enable EPEL repository for freetds and hiredis RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm +# Enable powertools repo RUN yum config-manager --enable powertools -# Currently needed for hiredis-devel + +# Enable epel-testing, currently needed for hiredis-devel RUN yum config-manager --enable epel-testing # # Install build dependencies # -RUN [ -e redhat/freeradius.spec ] && yum-builddep -y redhat/freeradius.spec +# Run twice, it doesn't always get everything with one invocation +# +RUN [ -e redhat/freeradius.spec ] && \ + yum-builddep -y redhat/freeradius.spec && \ + yum-builddep -y redhat/freeradius.spec # # Create RPM build environment @@ -57,9 +83,11 @@ ENV BUILDDIR=/root/rpmbuild RUN rpmdev-setuptree RUN ./configure -RUN make freeradius-server-$(cat VERSION).tar.bz2 -RUN cp freeradius-server-$(cat VERSION).tar.bz2 $BUILDDIR/SOURCES/ +RUN cp VERSION /VERSION +RUN make freeradius-server-$(cat /VERSION).tar.bz2 +RUN cp freeradius-server-$(cat /VERSION).tar.bz2 $BUILDDIR/SOURCES/ RUN cp -r redhat/* $BUILDDIR/SOURCES/ +RUN sed -i "s/^Version:.*/Version: $(cat /VERSION)/" redhat/freeradius.spec RUN cp -r redhat/freeradius.spec $BUILDDIR/SPECS/ WORKDIR $BUILDDIR @@ -67,7 +95,7 @@ WORKDIR $BUILDDIR # Build the server # ENV QA_RPATHS=0x0003 -RUN rpmbuild -bb --define '_release $release' "$BUILDDIR/SPECS/freeradius.spec" +RUN rpmbuild -bb --define "_release $(cat /VERSION)" "$BUILDDIR/SPECS/freeradius.spec" RUN mkdir /root/rpms RUN mv $BUILDDIR/RPMS/*/*.rpm /root/rpms/ @@ -76,8 +104,14 @@ RUN mv $BUILDDIR/RPMS/*/*.rpm /root/rpms/ # Clean environment and run the server # FROM ${from} + COPY --from=build /root/rpms /tmp/ +# +# Install yum +# +RUN dnf install -y yum + # Use LTB's openldap packages intead of the distribution version to avoid linking against NSS RUN echo $'[ltb-project]\n\ name=LTB project packages\n\ @@ -86,11 +120,13 @@ enabled=1\n\ gpgcheck=1\n\ gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ > /etc/yum.repos.d/ltb-project.repo \ - && rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project \ - \ -# EPEL repository for freetds and hiredis - && yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ + && rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project + + +# EPEL repository for freetds and hiredis +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-8.noarch.rpm \ && yum install -y dnf-utils \ + && yum config-manager --enable powertools \ && yum config-manager --enable epel-testing ARG radiusd_uid=95 @@ -100,8 +136,9 @@ RUN groupadd -g ${radiusd_gid} -r radiusd \ && useradd -u ${radiusd_uid} -g radiusd -r -M -d /home/radiusd -s /sbin/nologin radiusd \ && yum install -y /tmp/*.rpm -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh +WORKDIR / +COPY scripts/docker//etc/docker-entrypoint.sh.rpm docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh EXPOSE 1812/udp 1813/udp ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/scripts/docker/rocky8/docker-entrypoint.sh b/scripts/docker/dists/rocky8/docker-entrypoint.sh index 900ad6b..900ad6b 100755 --- a/scripts/docker/rocky8/docker-entrypoint.sh +++ b/scripts/docker/dists/rocky8/docker-entrypoint.sh diff --git a/scripts/docker/dists/rocky9/Dockerfile b/scripts/docker/dists/rocky9/Dockerfile new file mode 100644 index 0000000..0f57010 --- /dev/null +++ b/scripts/docker/dists/rocky9/Dockerfile @@ -0,0 +1,124 @@ +# Auto generated for rocky9 +# from scripts/docker/m4/Dockerfile.rpm.m4 +# +# Rebuild this file with `make docker.rocky9.regen` +# +ARG from=rockylinux/rockylinux:9 +FROM ${from} as build + +# +# Install yum +# +RUN dnf install -y yum + +RUN rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9 + +# +# Install build tools +# +RUN yum groupinstall -y "Development Tools" + +RUN yum install -y rpmdevtools openssl dnf-utils + + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Other requirements +# + +# Enable EPEL repository for freetds and hiredis +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm + +# Enable Code Ready Builder repo (CentOS powertools equivalent) +RUN yum install -y yum-utils +RUN yum config-manager --enable crb + +# +# Install build dependencies +# +# Run twice, it doesn't always get everything with one invocation +# +RUN [ -e redhat/freeradius.spec ] && \ + yum-builddep -y redhat/freeradius.spec && \ + yum-builddep -y redhat/freeradius.spec + +# +# Create RPM build environment +# +ENV BUILDDIR=/root/rpmbuild +RUN rpmdev-setuptree + +RUN ./configure +RUN cp VERSION /VERSION +RUN make freeradius-server-$(cat /VERSION).tar.bz2 +RUN cp freeradius-server-$(cat /VERSION).tar.bz2 $BUILDDIR/SOURCES/ +RUN cp -r redhat/* $BUILDDIR/SOURCES/ +RUN sed -i "s/^Version:.*/Version: $(cat /VERSION)/" redhat/freeradius.spec +RUN cp -r redhat/freeradius.spec $BUILDDIR/SPECS/ +WORKDIR $BUILDDIR + +# +# Build the server +# +ENV QA_RPATHS=0x0003 +RUN rpmbuild -bb --define "_release $(cat /VERSION)" "$BUILDDIR/SPECS/freeradius.spec" + +RUN mkdir /root/rpms +RUN mv $BUILDDIR/RPMS/*/*.rpm /root/rpms/ + +# +# Clean environment and run the server +# +FROM ${from} + +COPY --from=build /root/rpms /tmp/ + +# +# Install yum +# +RUN dnf install -y yum + + + +# EPEL repository for freetds and hiredis +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm \ + && yum install -y dnf-utils \ + && yum config-manager --enable crb \ + && yum config-manager --enable epel-testing + +ARG radiusd_uid=95 +ARG radiusd_gid=95 + +RUN groupadd -g ${radiusd_gid} -r radiusd \ + && useradd -u ${radiusd_uid} -g radiusd -r -M -d /home/radiusd -s /sbin/nologin radiusd \ + && yum install -y /tmp/*.rpm + +WORKDIR / +COPY scripts/docker//etc/docker-entrypoint.sh.rpm docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["radiusd"] diff --git a/scripts/docker/dists/rocky9/docker-entrypoint.sh b/scripts/docker/dists/rocky9/docker-entrypoint.sh new file mode 100755 index 0000000..900ad6b --- /dev/null +++ b/scripts/docker/dists/rocky9/docker-entrypoint.sh @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +# this if will check if the first argument is a flag +# but only works if all arguments require a hyphenated flag +# -v; -SL; -f arg; etc will work, but not arg1 arg2 +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- radiusd "$@" +fi + +# check for the expected command +if [ "$1" = 'radiusd' ]; then + shift + exec radiusd -f "$@" +fi + +# debian people are likely to call "freeradius" as well, so allow that +if [ "$1" = 'freeradius' ]; then + shift + exec radiusd -f "$@" +fi + +# else default to run whatever the user wanted like "bash" or "sh" +exec "$@" diff --git a/scripts/docker/dists/ubuntu18/Dockerfile b/scripts/docker/dists/ubuntu18/Dockerfile new file mode 100644 index 0000000..4e32632 --- /dev/null +++ b/scripts/docker/dists/ubuntu18/Dockerfile @@ -0,0 +1,82 @@ +# Auto generated for ubuntu18 +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.ubuntu18.regen` +# +ARG from=ubuntu:18.04 +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +RUN apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/ubuntu18/docker-entrypoint.sh b/scripts/docker/dists/ubuntu18/docker-entrypoint.sh index 93141b0..93141b0 100755 --- a/scripts/docker/ubuntu18/docker-entrypoint.sh +++ b/scripts/docker/dists/ubuntu18/docker-entrypoint.sh diff --git a/scripts/docker/dists/ubuntu20/Dockerfile b/scripts/docker/dists/ubuntu20/Dockerfile new file mode 100644 index 0000000..4a3bbf4 --- /dev/null +++ b/scripts/docker/dists/ubuntu20/Dockerfile @@ -0,0 +1,82 @@ +# Auto generated for ubuntu20 +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.ubuntu20.regen` +# +ARG from=ubuntu:20.04 +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +RUN apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/ubuntu20/docker-entrypoint.sh b/scripts/docker/dists/ubuntu20/docker-entrypoint.sh index 93141b0..93141b0 100755 --- a/scripts/docker/ubuntu20/docker-entrypoint.sh +++ b/scripts/docker/dists/ubuntu20/docker-entrypoint.sh diff --git a/scripts/docker/dists/ubuntu22/Dockerfile b/scripts/docker/dists/ubuntu22/Dockerfile new file mode 100644 index 0000000..778112a --- /dev/null +++ b/scripts/docker/dists/ubuntu22/Dockerfile @@ -0,0 +1,87 @@ +# Auto generated for ubuntu22 +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.ubuntu22.regen` +# +ARG from=ubuntu:22.04 +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +ARG freerad_uid=101 +ARG freerad_gid=101 + +RUN groupadd -g ${freerad_gid} -r freerad \ + && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ + && apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/ubuntu22/docker-entrypoint.sh b/scripts/docker/dists/ubuntu22/docker-entrypoint.sh index 93141b0..93141b0 100755 --- a/scripts/docker/ubuntu22/docker-entrypoint.sh +++ b/scripts/docker/dists/ubuntu22/docker-entrypoint.sh diff --git a/scripts/docker/dists/ubuntu24/Dockerfile b/scripts/docker/dists/ubuntu24/Dockerfile new file mode 100644 index 0000000..ad4520e --- /dev/null +++ b/scripts/docker/dists/ubuntu24/Dockerfile @@ -0,0 +1,87 @@ +# Auto generated for ubuntu24 +# from scripts/docker/m4/Dockerfile.deb.m4 +# +# Rebuild this file with `make docker.ubuntu24.regen` +# +ARG from=ubuntu:24.04 +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +ARG freerad_uid=101 +ARG freerad_gid=101 + +RUN groupadd -g ${freerad_gid} -r freerad \ + && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ + && apt-get update \ + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.deb docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/docker.mk b/scripts/docker/docker.mk index 9773625..bf556c6 100644 --- a/scripts/docker/docker.mk +++ b/scripts/docker/docker.mk @@ -25,8 +25,8 @@ # version, DOCKER_COMMIT _must_ also be set. DOCKER_VERSION := $(RADIUSD_VERSION_STRING) # -# Commit hash/tag/branch to build, will be taken from VERSION above if not overridden, e.g. "release_3_2_0" -DOCKER_COMMIT := release_$(shell echo $(DOCKER_VERSION) | tr .- __) +# Commit hash/tag/branch to build, if not set then HEAD will be used. +DOCKER_COMMIT := # # Build args, most likely "--no-cache" DOCKER_BUILD_ARGS := @@ -40,6 +40,22 @@ DOCKER_REPO := freeradius # Registry to push to DOCKER_REGISTRY := # +# Location of Docker-related files +DOCKER_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +DIST_DIR := $(DOCKER_DIR)/dists +# +# List of images we can build +DOCKER_IMAGES:=$(sort $(patsubst $(DIST_DIR)/%,%,$(wildcard $(DIST_DIR)/*))) + +DOCKER_DEFAULT_UBUNTU := ubuntu22 +DOCKER_DEFAULT_ALPINE := alpine + +ifeq "${VERBOSE}" "" + Q=@ +else + Q= +endif + ifneq "$(DOCKER_REPO)" "" override DOCKER_REPO := $(DOCKER_REPO)/ @@ -50,37 +66,146 @@ ifneq "$(DOCKER_REGISTRY)" "" endif +# +# Print some useful help +# +.PHONY: docker.help.images +docker.help.images: + @echo Available images: $(DOCKER_IMAGES) + +.PHONY: docker.help +docker.help: docker.help.images + @echo "" + @echo "Make targets:" + @echo " docker-ubuntu - build main ubuntu image" + @echo " docker-alpine - build main alpine image" + @echo " docker.regen - regenerate all Dockerfiles from templates" + @echo "" + @echo "Make targets per image:" + @echo " docker.IMAGE.build - build image" + @echo " docker.IMAGE.regen - regenerate Dockerfile" + @echo "" + @echo "Arguments:" + @echo ' DOCKER_BUILD_ARGS="--no-cache" - extra build args' + @echo ' DOCKER_REGISTRY="docker.example.com" - registry to build for' + @echo ' DOCKER_REPO="freeradius" - docker repo name' + @echo ' DOCKER_TAG="freeradius-server" - docker tag name' + @echo ' DOCKER_COMMIT="HEAD" - commit/ref to build from' + @echo ' DOCKER_VERSION="$(DOCKER_VERSION)" - version for docker image name' + + +# +# Rules for each OS +# + +define ADD_DOCKER_RULES + $$(DIST_DIR)/${1}/Dockerfile: $(DOCKER_DIR)/m4/Dockerfile.m4 $(DOCKER_DIR)/m4/Dockerfile.deb.m4 $(DOCKER_DIR)/m4/Dockerfile.rpm.m4 $(DOCKER_DIR)/m4/Dockerfile.alpine.m4 $(DOCKER_DIR)/docker.mk + $$(Q)echo REGEN ${1}/Dockerfile + $$(Q)m4 -I $(DOCKER_DIR)/m4 -D D_NAME=${1} -D D_TYPE=docker $$< > $$@ + + DOCKER_DOCKERFILES += $$(DIST_DIR)/${1}/Dockerfile + + .PHONY: docker.${1}.regen + docker.${1}.regen: $$(DIST_DIR)/${1}/Dockerfile + + .PHONY: docker.${1}.build + docker.${1}.build: + @echo BUILD ${1} $(DOCKER_COMMIT) + $(Q)docker buildx build \ + $(DOCKER_BUILD_ARGS) \ + --progress=plain \ + --build-arg=release=$(DOCKER_COMMIT) \ + -t $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-${1} \ + -f $(DIST_DIR)/${1}/Dockerfile \ + . + +endef + +$(foreach IMAGE,$(DOCKER_IMAGES), \ + $(eval $(call ADD_DOCKER_RULES,$(IMAGE)))) + +.PHONY: docker.regen +docker.regen: $(DOCKER_DOCKERFILES) + + +# +# Rules to rebuild Docker images +# .PHONY: docker-ubuntu -docker-ubuntu: - @echo Building ubuntu $(DOCKER_COMMIT) - $(Q)docker build $(DOCKER_BUILD_ARGS) scripts/docker/ubuntu22 --build-arg=release=$(DOCKER_COMMIT) -t $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION) +docker-ubuntu: docker.$(DOCKER_DEFAULT_UBUNTU).build + $(Q)docker image tag \ + $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-$(DOCKER_DEFAULT_UBUNTU) \ + $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION) .PHONY: docker-alpine -docker-alpine: - @echo Building alpine $(DOCKER_COMMIT) - $(Q)docker build $(DOCKER_BUILD_ARGS) scripts/docker/alpine --build-arg=release=$(DOCKER_COMMIT) -t $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-alpine +docker-alpine: docker.$(DOCKER_DEFAULT_ALPINE).build + $(Q)docker image tag \ + $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-$(DOCKER_DEFAULT_ALPINE) \ + $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-alpine .PHONY: docker docker: docker-ubuntu docker-alpine -.PHONY: docker-push -docker-push: docker +# +# Push main ubuntu and alpine images (all below are separate for CI jobs) +# +.PHONY: docker-push-ubuntu +docker-push-ubuntu: $(Q)docker push $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION) + +.PHONY: docker-push-alpine +docker-push-alpine: $(Q)docker push $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-alpine -.PHONY: docker-tag-latest -docker-tag-latest: docker +.PHONY: docker-push +docker-push: docker-push-ubuntu docker-push-alpine + +# +# Tag main "latest" images +# +.PHONY: docker-tag-latest-ubuntu +docker-tag-latest-ubuntu: $(Q)docker tag $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION) $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest - $(Q)docker tag $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-alpine $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-alpine $(Q)docker tag $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION) $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-3.2 + +.PHONY: docker-tag-latest-alpine +docker-tag-latest-alpine: + $(Q)docker tag $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-alpine $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-alpine $(Q)docker tag $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION)-alpine $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-3.2-alpine -.PHONY: docker-push-latest -docker-push-latest: docker-push docker-tag-latest +.PHONY: docker-tag-latest +docker-tag-latest: docker-tag-latest-ubuntu docker-tag-latest-alpine + +# +# Push main "latest" images +# +.PHONY: docker-push-latest-ubuntu +docker-push-latest-ubuntu: docker-push-ubuntu docker-tag-latest-ubuntu $(Q)docker push $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest - $(Q)docker push $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-alpine $(Q)docker push $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-3.2 + +.PHONY: docker-push-latest-alpine +docker-push-latest-alpine: docker-push-alpine docker-tag-latest-alpine + $(Q)docker push $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-alpine $(Q)docker push $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):latest-3.2-alpine +.PHONY: docker-push-latest +docker-push-latest: docker-push-latest-ubuntu docker-push-latest-alpine + +# +# Convenience target to do everything +# .PHONY: docker-publish -docker-publish: docker-push-latest +docker-publish: docker docker-push-latest + +# +# Used for multi-arch CI job. "docker manifest" rather than "docker buildx +# --platforms=...,..." so that we can parallelise the build in GH Actions. +# +.PHONY: docker-ci-manifest +docker-ci-manifest: + $(Q)docker manifest create \ + $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION) \ + $(foreach ARCH,$(DOCKER_ARCHS),--amend $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(ARCH)-$(DOCKER_VERSION)) + $(Q)docker manifest push \ + $(DOCKER_REGISTRY)$(DOCKER_REPO)$(DOCKER_TAG):$(DOCKER_VERSION) diff --git a/scripts/docker/etc/docker-entrypoint.sh.alpine b/scripts/docker/etc/docker-entrypoint.sh.alpine new file mode 100755 index 0000000..e0f9f6f --- /dev/null +++ b/scripts/docker/etc/docker-entrypoint.sh.alpine @@ -0,0 +1,27 @@ +#!/bin/sh +set -e + +PATH=/opt/sbin:/opt/bin:$PATH +export PATH + +# this if will check if the first argument is a flag +# but only works if all arguments require a hyphenated flag +# -v; -SL; -f arg; etc will work, but not arg1 arg2 +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- radiusd "$@" +fi + +# check for the expected command +if [ "$1" = 'radiusd' ]; then + shift + exec radiusd -f "$@" +fi + +# debian people are likely to call "freeradius" as well, so allow that +if [ "$1" = 'freeradius' ]; then + shift + exec radiusd -f "$@" +fi + +# else default to run whatever the user wanted like "bash" or "sh" +exec "$@" diff --git a/scripts/docker/etc/docker-entrypoint.sh.deb b/scripts/docker/etc/docker-entrypoint.sh.deb new file mode 100755 index 0000000..93141b0 --- /dev/null +++ b/scripts/docker/etc/docker-entrypoint.sh.deb @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +# this if will check if the first argument is a flag +# but only works if all arguments require a hyphenated flag +# -v; -SL; -f arg; etc will work, but not arg1 arg2 +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- freeradius "$@" +fi + +# check for the expected command +if [ "$1" = 'freeradius' ]; then + shift + exec freeradius -f "$@" +fi + +# many people are likely to call "radiusd" as well, so allow that +if [ "$1" = 'radiusd' ]; then + shift + exec freeradius -f "$@" +fi + +# else default to run whatever the user wanted like "bash" or "sh" +exec "$@" diff --git a/scripts/docker/etc/docker-entrypoint.sh.rpm b/scripts/docker/etc/docker-entrypoint.sh.rpm new file mode 100755 index 0000000..900ad6b --- /dev/null +++ b/scripts/docker/etc/docker-entrypoint.sh.rpm @@ -0,0 +1,24 @@ +#!/bin/sh +set -e + +# this if will check if the first argument is a flag +# but only works if all arguments require a hyphenated flag +# -v; -SL; -f arg; etc will work, but not arg1 arg2 +if [ "$#" -eq 0 ] || [ "${1#-}" != "$1" ]; then + set -- radiusd "$@" +fi + +# check for the expected command +if [ "$1" = 'radiusd' ]; then + shift + exec radiusd -f "$@" +fi + +# debian people are likely to call "freeradius" as well, so allow that +if [ "$1" = 'freeradius' ]; then + shift + exec radiusd -f "$@" +fi + +# else default to run whatever the user wanted like "bash" or "sh" +exec "$@" diff --git a/scripts/docker/alpine/Dockerfile b/scripts/docker/m4/Dockerfile.alpine.m4 index 2965525..64c6c8f 100644 --- a/scripts/docker/alpine/Dockerfile +++ b/scripts/docker/m4/Dockerfile.alpine.m4 @@ -1,4 +1,4 @@ -ARG from=alpine:3.13 +ARG from=DOCKER_IMAGE FROM ${from} as build # @@ -10,17 +10,23 @@ RUN apk add git gcc make # # Create build directory # -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ # -# Shallow clone the FreeRADIUS source +# Copy the FreeRADIUS directory in # -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +RUN [ -z "$release" ] || git checkout ${release} -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server # # Install build dependencies @@ -33,7 +39,7 @@ RUN apk add linux-headers RUN apk add pcre-dev libidn-dev krb5-dev samba-dev curl-dev json-c-dev RUN apk add openldap-dev unbound-dev # languages -RUN apk add ruby-dev perl-dev python2-dev +RUN apk add ruby-dev perl-dev python2-dev python3-dev # databases RUN apk add hiredis-dev libmemcached-dev gdbm-dev libcouchbase-dev # sql @@ -69,14 +75,15 @@ RUN apk update \ && apk add libcurl json-c libldap hiredis sqlite-dev \ #RUN apk add libidn krb5 #RUN apk add unbound-libs -#RUN apk add ruby-libs perl python2-dev +#RUN apk add ruby-libs perl python2-dev python3-dev #RUN apk add libmemcached gdbm libcouchbase #RUN apk add postgresql-dev mariadb-dev unixodbc-dev \ && ln -s /opt/etc/raddb /etc/raddb -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh +WORKDIR / +COPY DOCKER_TOPDIR/etc/docker-entrypoint.sh.PKG_TYPE docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh EXPOSE 1812/udp 1813/udp ENTRYPOINT ["/docker-entrypoint.sh"] diff --git a/scripts/docker/m4/Dockerfile.deb.m4 b/scripts/docker/m4/Dockerfile.deb.m4 new file mode 100644 index 0000000..0b4e5b5 --- /dev/null +++ b/scripts/docker/m4/Dockerfile.deb.m4 @@ -0,0 +1,88 @@ +ARG from=DOCKER_IMAGE +FROM ${from} as build + +ARG DEBIAN_FRONTEND=noninteractive + +# +# Install build tools +# +RUN apt-get update +RUN apt-get install -y devscripts equivs git quilt gcc + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Install build dependencies +# +RUN if [ -e ./debian/control.in ]; then \ + debian/rules debian/control; \ + fi; \ + echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control + +# +# Build the server +# +# Work around fakeroot problems in Docker when building for different +# platforms - doesn't matter as we run as root in the container anyway. +# +#RUN make -j$(nproc) deb +RUN debian/rules debian/control \ + && dpkg-buildpackage --jobs=auto -b -uc + +# +# Clean environment and run the server +# +FROM ${from} +ARG DEBIAN_FRONTEND=noninteractive + +COPY --from=build /usr/local/src/repositories/*.deb /tmp/ + +RUN ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime + +ifelse(ifelse( + D_NAME, `debian10', no, + D_NAME, `ubuntu18', no, + D_NAME, `ubuntu20', no, + yes), yes, `dnl +ARG freerad_uid=101 +ARG freerad_gid=101 + +RUN groupadd -g ${freerad_gid} -r freerad \ + && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ + && apt-get update \', +`RUN apt-get update \') + && apt-get install -y tzdata \ + && apt-get install -y /tmp/*.deb \ + && apt-get clean \ + && rm -r /var/lib/apt/lists/* /tmp/*.deb \ + \ + && ln -s /etc/freeradius /etc/raddb + +WORKDIR / +COPY scripts/docker/etc/docker-entrypoint.sh.PKG_TYPE docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["freeradius"] diff --git a/scripts/docker/m4/Dockerfile.m4 b/scripts/docker/m4/Dockerfile.m4 new file mode 100644 index 0000000..f9890a1 --- /dev/null +++ b/scripts/docker/m4/Dockerfile.m4 @@ -0,0 +1,45 @@ +dnl Look up the OS codename, docker base image etc before including +dnl the main Dockerfile template. +dnl +dnl This top-level template is used by both the docker makefile +dnl (scripts/docker/docker.mk) and the crossbuild makefile +dnl (scripts/crossbuild/crossbuild.mk), but the Dockerfile templates +dnl they use are different - see the m4 directories for each. +dnl +divert(`-1') +changequote(`[', `]') +define([DOCKER_TOPDIR], [scripts/docker/]) +define([p_SET], [ + define([PKG_TYPE], [$1]) + define([OS_NAME], [$2]) + define([OS_VER], [$3]) + define([OS_CODENAME], [$4]) + define([DOCKER_IMAGE], [$5]) +]) +dnl D_NAME PKG_TYPE OS_NAME OS_VER OS_CODENAME DOCKER_IMAGE +ifelse( + D_NAME, [alpine], [p_SET([alpine], [alpine], [3.13], [alpine], [alpine:3.13])], + D_NAME, [debian10], [p_SET([deb], [debian], [10], [buster], [debian:buster])], + D_NAME, [debian11], [p_SET([deb], [debian], [11], [bullseye], [debian:bullseye])], + D_NAME, [debian12], [p_SET([deb], [debian], [12], [bookworm], [debian:bookworm])], + D_NAME, [debiansid], [p_SET([deb], [debian], [99], [sid], [debian:sid])], + D_NAME, [ubuntu18], [p_SET([deb], [ubuntu], [18], [bionic], [ubuntu:18.04])], + D_NAME, [ubuntu20], [p_SET([deb], [ubuntu], [20], [focal], [ubuntu:20.04])], + D_NAME, [ubuntu22], [p_SET([deb], [ubuntu], [22], [jammy], [ubuntu:22.04])], + D_NAME, [ubuntu24], [p_SET([deb], [ubuntu], [24], [noble], [ubuntu:24.04])], + D_NAME, [centos7], [p_SET([rpm], [centos], [7], [7], [centos:7])], + D_NAME, [centos8], [p_SET([rpm], [centos], [8], [8], [centos:8])], + D_NAME, [rocky8], [p_SET([rpm], [rocky], [8], [8], [rockylinux/rockylinux:8])], + D_NAME, [rocky9], [p_SET([rpm], [rocky], [9], [9], [rockylinux/rockylinux:9])], + [errprint(error: OS 'D_NAME' not defined[,] see __file__ +)m4exit(1)] +) +undefine([p_SET]) +divert[]dnl +[#] Auto generated for D_NAME +[#] from scripts/D_TYPE/m4/Dockerfile.PKG_TYPE.m4 +[#] +[#] Rebuild this file with `make D_TYPE.D_NAME.regen` +[#] +changequote([`], ['])dnl +include(Dockerfile.PKG_TYPE.m4)dnl diff --git a/scripts/docker/m4/Dockerfile.rpm.m4 b/scripts/docker/m4/Dockerfile.rpm.m4 new file mode 100644 index 0000000..03181e8 --- /dev/null +++ b/scripts/docker/m4/Dockerfile.rpm.m4 @@ -0,0 +1,186 @@ +ARG from=DOCKER_IMAGE +FROM ${from} as build + +ifelse(OS_VER, 7, `dnl +# +# CentOS 7 is now EOL, so we need to fix up the repo source +# +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* +')dnl + +ifelse(OS_VER, `7', `', `dnl +# +# Install yum +# +RUN dnf install -y yum +')dnl + +ifelse(OS_VER, 8, `dnl +RUN rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-rockyofficial +')dnl +ifelse(OS_VER, 9, `dnl +RUN rpmkeys --import /etc/pki/rpm-gpg/RPM-GPG-KEY-Rocky-9 +') +# +# Install build tools +# +RUN yum groupinstall -y "Development Tools" +ifelse(OS_VER, 7,`dnl +RUN yum install -y rpmdevtools +RUN yum install -y openssl +',` +RUN yum install -y rpmdevtools openssl dnf-utils +') + +# +# Create build directory +# +RUN mkdir -p /usr/local/src/repositories/freeradius-server +WORKDIR /usr/local/src/repositories/freeradius-server/ + +# +# Copy the FreeRADIUS directory in +# +COPY . . + +# +# Clean up tree - we want to build from the latest commit, not from +# any cruft left around on the local system +# +RUN git clean -fdxx \ + && git reset --hard HEAD + +ARG release +RUN [ -z "$release" ] || git checkout ${release} ; \ + git status ; \ + git log -1 --oneline + +# +# Other requirements +# +changequote(`{', `}')dnl +ifelse(ifelse(OS_VER, 7, yes, OS_VER, 8, yes, no), yes, { +# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS +RUN echo $'[ltb-project]\n\ +name=LTB project packages\n\ +baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ +enabled=1\n\ +gpgcheck=1\n\ +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ +> /etc/yum.repos.d/ltb-project.repo +RUN rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project +})dnl +changequote({`}, {'})dnl + +# Enable EPEL repository for freetds and hiredis +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-OS_VER.noarch.rpm +ifelse(OS_VER, 8, ` +# Enable powertools repo +RUN yum config-manager --enable powertools + +# Enable epel-testing, currently needed for hiredis-devel +RUN yum config-manager --enable epel-testing +')dnl +ifelse(OS_VER, 9, ` +# Enable Code Ready Builder repo (CentOS powertools equivalent) +RUN yum install -y yum-utils +RUN yum config-manager --enable crb +')dnl + +# +# Install build dependencies +# +# Run twice, it doesn't always get everything with one invocation +# +RUN [ -e redhat/freeradius.spec ] && \ + yum-builddep -y redhat/freeradius.spec && \ + yum-builddep -y redhat/freeradius.spec + +# +# Create RPM build environment +# +ENV BUILDDIR=/root/rpmbuild +RUN rpmdev-setuptree + +RUN ./configure +RUN cp VERSION /VERSION +RUN make freeradius-server-$(cat /VERSION).tar.bz2 +RUN cp freeradius-server-$(cat /VERSION).tar.bz2 $BUILDDIR/SOURCES/ +RUN cp -r redhat/* $BUILDDIR/SOURCES/ +RUN sed -i "s/^Version:.*/Version: $(cat /VERSION)/" redhat/freeradius.spec +RUN cp -r redhat/freeradius.spec $BUILDDIR/SPECS/ +WORKDIR $BUILDDIR + +# +# Build the server +# +ENV QA_RPATHS=0x0003 +RUN rpmbuild -bb --define "_release $(cat /VERSION)" "$BUILDDIR/SPECS/freeradius.spec" + +RUN mkdir /root/rpms +RUN mv $BUILDDIR/RPMS/*/*.rpm /root/rpms/ + +# +# Clean environment and run the server +# +FROM ${from} + +COPY --from=build /root/rpms /tmp/ + +ifelse(OS_VER, 7, `dnl +# +# CentOS 7 is now EOL, so we need to fix up the repo source +# +RUN sed -i "s/^mirrorlist/#mirrorlist/g" /etc/yum.repos.d/CentOS-* +RUN sed -i "s|#\s*baseurl=http://mirror.centos.org|baseurl=http://vault.centos.org|g" /etc/yum.repos.d/CentOS-* +')dnl + +ifelse(OS_VER, `7', `', `dnl +# +# Install yum +# +RUN dnf install -y yum +')dnl + +changequote(`{', `}')dnl +ifelse(ifelse(OS_VER, 7, yes, OS_VER, 8, yes, no), yes, {dnl +# Use LTB's openldap packages intead of the distribution version to avoid linking against NSS +RUN echo $'[ltb-project]\n\ +name=LTB project packages\n\ +baseurl=https://ltb-project.org/rpm/$releasever/$basearch\n\ +enabled=1\n\ +gpgcheck=1\n\ +gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-LTB-project'\ +> /etc/yum.repos.d/ltb-project.repo \ + && rpm --import https://ltb-project.org/lib/RPM-GPG-KEY-LTB-project +})dnl +changequote({`}, {'})dnl + + +# EPEL repository for freetds and hiredis +RUN yum install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-OS_VER.noarch.rpm \ +ifelse(OS_VER, 7, ` \', `dnl + && yum install -y dnf-utils \ +ifelse(OS_VER, 8, `dnl + && yum config-manager --enable powertools \ +')dnl +ifelse(OS_VER, 9, `dnl + && yum config-manager --enable crb \ +')dnl + && yum config-manager --enable epel-testing + +ARG radiusd_uid=95 +ARG radiusd_gid=95 + +RUN groupadd -g ${radiusd_gid} -r radiusd \ + && useradd -u ${radiusd_uid} -g radiusd -r -M -d /home/radiusd -s /sbin/nologin radiusd \') + && yum install -y /tmp/*.rpm + +WORKDIR / +COPY DOCKER_TOPDIR/etc/docker-entrypoint.sh.PKG_TYPE docker-entrypoint.sh +RUN chmod +x docker-entrypoint.sh + +EXPOSE 1812/udp 1813/udp +ENTRYPOINT ["/docker-entrypoint.sh"] +CMD ["radiusd"] diff --git a/scripts/docker/ubuntu18/Dockerfile b/scripts/docker/ubuntu18/Dockerfile deleted file mode 100644 index 7322026..0000000 --- a/scripts/docker/ubuntu18/Dockerfile +++ /dev/null @@ -1,59 +0,0 @@ -ARG from=ubuntu:18.04 -FROM ${from} as build - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install build tools -# -RUN apt-get update -RUN apt-get install -y devscripts equivs git quilt gcc - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Install build dependencies -# -RUN git checkout ${release}; \ - if [ -e ./debian/control.in ]; then \ - debian/rules debian/control; \ - fi; \ - echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control - -# -# Build the server -# -RUN make -j2 deb - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /usr/local/src/repositories/*.deb /tmp/ - -RUN apt-get update \ - && apt-get install -y /tmp/*.deb \ - && apt-get clean \ - && rm -r /var/lib/apt/lists/* /tmp/*.deb \ - \ - && ln -s /etc/freeradius /etc/raddb - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["freeradius"] diff --git a/scripts/docker/ubuntu20/Dockerfile b/scripts/docker/ubuntu20/Dockerfile deleted file mode 100644 index 783ebc9..0000000 --- a/scripts/docker/ubuntu20/Dockerfile +++ /dev/null @@ -1,61 +0,0 @@ -ARG from=ubuntu:20.04 -FROM ${from} as build - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install build tools -# -RUN apt-get update -RUN apt-get install -y devscripts equivs git quilt gcc - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Install build dependencies -# -RUN git checkout ${release}; \ - if [ -e ./debian/control.in ]; then \ - debian/rules debian/control; \ - fi; \ - echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control - -# -# Build the server -# -RUN make -j2 deb - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /usr/local/src/repositories/*.deb /tmp/ - -ARG DEBIAN_FRONTEND=noninteractive - -RUN apt-get update \ - && apt-get install -y /tmp/*.deb \ - && apt-get clean \ - && rm -r /var/lib/apt/lists/* /tmp/*.deb \ - \ - && ln -s /etc/freeradius /etc/raddb - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["freeradius"] diff --git a/scripts/docker/ubuntu22/Dockerfile b/scripts/docker/ubuntu22/Dockerfile deleted file mode 100644 index 9e6aa57..0000000 --- a/scripts/docker/ubuntu22/Dockerfile +++ /dev/null @@ -1,66 +0,0 @@ -ARG from=ubuntu:22.04 -FROM ${from} as build - -ARG DEBIAN_FRONTEND=noninteractive - -# -# Install build tools -# -RUN apt-get update -RUN apt-get install -y devscripts equivs git quilt gcc - -# -# Create build directory -# -RUN mkdir -p /usr/local/src/repositories -WORKDIR /usr/local/src/repositories - -# -# Shallow clone the FreeRADIUS source -# -ARG source=https://github.com/FreeRADIUS/freeradius-server.git -ARG release=v3.2.x - -RUN git clone --depth 1 --single-branch --branch ${release} ${source} -WORKDIR freeradius-server - -# -# Install build dependencies -# -RUN git checkout ${release}; \ - if [ -e ./debian/control.in ]; then \ - debian/rules debian/control; \ - fi; \ - echo 'y' | mk-build-deps -irt'apt-get -yV' debian/control - -# -# Build the server -# -RUN make -j2 deb - -# -# Clean environment and run the server -# -FROM ${from} -COPY --from=build /usr/local/src/repositories/*.deb /tmp/ - -ARG freerad_uid=101 -ARG freerad_gid=101 - -ARG DEBIAN_FRONTEND=noninteractive - -RUN groupadd -g ${freerad_gid} -r freerad \ - && useradd -u ${freerad_uid} -g freerad -r -M -d /etc/freeradius -s /usr/sbin/nologin freerad \ - && apt-get update \ - && apt-get install -y /tmp/*.deb \ - && apt-get clean \ - && rm -r /var/lib/apt/lists/* /tmp/*.deb \ - \ - && ln -s /etc/freeradius /etc/raddb - -COPY docker-entrypoint.sh / -RUN chmod +x /docker-entrypoint.sh - -EXPOSE 1812/udp 1813/udp -ENTRYPOINT ["/docker-entrypoint.sh"] -CMD ["freeradius"] diff --git a/scripts/snmp-proxy/freeradius-snmp.pl b/scripts/snmp-proxy/freeradius-snmp.pl index f30fc7d..9e70afc 100644 --- a/scripts/snmp-proxy/freeradius-snmp.pl +++ b/scripts/snmp-proxy/freeradius-snmp.pl @@ -40,7 +40,7 @@ my $cfg = { radius => { host => 'localhost', - port => 18120, + port => 18121, secret => 'adminsecret', # dictionary => '../radiusd/share/dictionary', dictionary => 'dictionary.hacked', @@ -167,7 +167,7 @@ sub radius_stats_get { # Wrappers for specific types of stats # sub radius_stats_get_global { return radius_stats_get(0x1f); } -sub radius_stats_get_client { return radius_stats_get(0x3f, 'FreeRADIUS-Stats-Client-Number' => $_[0]); } +sub radius_stats_get_client { return radius_stats_get(0x23, 'FreeRADIUS-Stats-Client-Number' => $_[0]); } # # Main loop of thread fetching status from freeradius server diff --git a/share/dictionary b/share/dictionary index f91d6b8..aeb5c8f 100644 --- a/share/dictionary +++ b/share/dictionary @@ -151,6 +151,7 @@ $INCLUDE dictionary.alcatel $INCLUDE dictionary.alcatel-lucent.aaa $INCLUDE dictionary.alcatel.esam $INCLUDE dictionary.alcatel.sr +$INCLUDE dictionary.alphion $INCLUDE dictionary.alteon $INCLUDE dictionary.altiga $INCLUDE dictionary.alvarion @@ -196,6 +197,7 @@ $INCLUDE dictionary.cisco.asa $INCLUDE dictionary.cisco.bbsm $INCLUDE dictionary.cisco.vpn5000 $INCLUDE dictionary.citrix +$INCLUDE dictionary.ckey $INCLUDE dictionary.clavister $INCLUDE dictionary.cnergee $INCLUDE dictionary.colubris @@ -271,6 +273,7 @@ $INCLUDE dictionary.nokia $INCLUDE dictionary.nomadix $INCLUDE dictionary.nortel $INCLUDE dictionary.ntua +$INCLUDE dictionary.openwifi $INCLUDE dictionary.packeteer $INCLUDE dictionary.paloalto $INCLUDE dictionary.patton @@ -296,6 +299,7 @@ $INCLUDE dictionary.shasta $INCLUDE dictionary.shiva $INCLUDE dictionary.siemens $INCLUDE dictionary.slipstream +$INCLUDE dictionary.smartsharesystems $INCLUDE dictionary.sofaware $INCLUDE dictionary.softbank $INCLUDE dictionary.sonicwall @@ -308,6 +312,7 @@ $INCLUDE dictionary.telebit $INCLUDE dictionary.telkom $INCLUDE dictionary.telrad $INCLUDE dictionary.terena +$INCLUDE dictionary.tplink $INCLUDE dictionary.trapeze $INCLUDE dictionary.travelping $INCLUDE dictionary.tripplite diff --git a/share/dictionary.3gpp2 b/share/dictionary.3gpp2 index 8fe3d6f..8085b95 100644 --- a/share/dictionary.3gpp2 +++ b/share/dictionary.3gpp2 @@ -85,7 +85,13 @@ ATTRIBUTE 3GPP2-Remote-IP-Address-Value 59.1 ipaddr ATTRIBUTE 3GPP2-Remote-IP-Address-Mask 59.2 integer ATTRIBUTE 3GPP2-Remote-IP-Qualifier 59.3 short -# 60 - 69 are marked "reserved" +ATTRIBUTE 3GPP2-HRPD-Access-Authorization 60 integer + +ATTRIBUTE 3GPP2-AT-Hardware-Identifier 61 tlv +ATTRIBUTE 3GPP2-AT-Hardware-Identifier-Type 61.1 integer +ATTRIBUTE 3GPP2-AT-Hardware-Identifier-Value 61.2 octets + +# 62 - 69 are marked "reserved" ATTRIBUTE 3GPP2-Remote-IPv6-Address 70 tlv ATTRIBUTE 3GPP2-Remote-IPv6-Address-Value 70.1 ipv6addr diff --git a/share/dictionary.alphion b/share/dictionary.alphion new file mode 100644 index 0000000..fe9403a --- /dev/null +++ b/share/dictionary.alphion @@ -0,0 +1,74 @@ +# -*- text -*- +# Copyright (C) 2024 The FreeRADIUS Server project and contributors +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# +############################################################################## +# +# $Id$ +# +############################################################################## + +VENDOR Alphion 8742 +BEGIN-VENDOR Alphion + +ATTRIBUTE Alphion-User-Role 1 string +ATTRIBUTE Alphion-User-Vlan 2 integer +ATTRIBUTE Alphion-Priv-Admin-User 3 integer +ATTRIBUTE Alphion-Admin-Role 4 string +ATTRIBUTE Alphion-Essid-Name 5 string +ATTRIBUTE Alphion-Location-Id 6 string +ATTRIBUTE Alphion-Port-Identifier 7 string +ATTRIBUTE Alphion-MMS-User-Template 8 string +ATTRIBUTE Alphion-Named-User-Vlan 9 string +ATTRIBUTE Alphion-AP-Group 10 string + +ATTRIBUTE Alphion-Framed-IPv6-Address 11 string +ATTRIBUTE Alphion-Device-Type 12 string +ATTRIBUTE Alphion-No-DHCP-Fingerprint 14 integer +ATTRIBUTE Alphion-Mdps-Device-Udid 15 string +ATTRIBUTE Alphion-Mdps-Device-Imei 16 string +ATTRIBUTE Alphion-Mdps-Device-Iccid 17 string +ATTRIBUTE Alphion-Mdps-Max-Devices 18 integer +ATTRIBUTE Alphion-Mdps-Device-Name 19 string +ATTRIBUTE Alphion-Mdps-Device-Product 20 string + +ATTRIBUTE Alphion-Mdps-Device-Version 21 string +ATTRIBUTE Alphion-Mdps-Device-Serial 22 string +ATTRIBUTE Alphion-CPPM-Role 23 string +ATTRIBUTE Alphion-AirGroup-User-Name 24 string +ATTRIBUTE Alphion-AirGroup-Shared-User 25 string +ATTRIBUTE Alphion-AirGroup-Shared-Role 26 string +ATTRIBUTE Alphion-AirGroup-Device-Type 27 integer +ATTRIBUTE Alphion-Auth-Survivability 28 string +ATTRIBUTE Alphion-AS-User-Name 29 string +ATTRIBUTE Alphion-AS-Credential-Hash 30 string + +ATTRIBUTE Alphion-WorkSpace-App-Name 31 string +ATTRIBUTE Alphion-Mdps-Provisioning-Settings 32 string +ATTRIBUTE Alphion-Mdps-Device-Profile 33 string + +ATTRIBUTE Alphion-AP-IP-Address 34 ipaddr + +ATTRIBUTE Alphion-AirGroup-Shared-Group 35 string +ATTRIBUTE Alphion-User-Group 36 string +ATTRIBUTE Alphion-Network-SSO-Token 37 string +ATTRIBUTE Alphion-AirGroup-Version 38 integer + +ATTRIBUTE Alphion-Port-Bounce-Host 40 integer + +ATTRIBUTE Alphion-Calea-Server-Ip 41 ipaddr +ATTRIBUTE Alphion-Admin-Path 42 string +ATTRIBUTE Alphion-Captive-Portal-URL 43 string +ATTRIBUTE Alphion-MPSK-Passphrase 44 octets encrypt=2 +ATTRIBUTE Alphion-ACL-Server-Query-Info 45 string +ATTRIBUTE Alphion-Command-String 46 string + +VALUE Alphion-AirGroup-Device-Type Personal-Device 1 +VALUE Alphion-AirGroup-Device-Type Shared-Device 2 +VALUE Alphion-AirGroup-Device-Type Deleted-Device 3 + +VALUE Alphion-AirGroup-Version AirGroup-v1 1 +VALUE Alphion-AirGroup-Version AirGroup-v2 2 + +END-VENDOR Alphion + diff --git a/share/dictionary.aruba b/share/dictionary.aruba index f4a4a03..26bd82a 100644 --- a/share/dictionary.aruba +++ b/share/dictionary.aruba @@ -67,14 +67,31 @@ ATTRIBUTE Aruba-QoS-Trust-Mode 52 integer ATTRIBUTE Aruba-UBT-Gateway-Role 53 string ATTRIBUTE Aruba-Gateway-Zone 54 string +ATTRIBUTE Aruba-DPP-Bootstrapping-Key-SHA256 55 string +ATTRIBUTE Aruba-DPP-Bootstrapping-Net-Access-Key-SHA256 56 string +ATTRIBUTE Aruba-DPP-Bootstrapping-Key-B64 57 string + ATTRIBUTE Aruba-STP-Admin-Edge-Port 58 integer ATTRIBUTE Aruba-UBT-Gateway-CPPM-Role 59 string -ATTRIBUTE Aruba-AP-MAC-Address 60 string -ATTRIBUTE Aruba-Device-MAC-Address 61 string +ATTRIBUTE Aruba-AP-MAC-Address 60 string +ATTRIBUTE Aruba-Device-MAC-Address 61 string +ATTRIBUTE Aruba-MPSK-Key-Name 62 string ATTRIBUTE Aruba-Device-Traffic-Class 63 integer +ATTRIBUTE Aruba-PVLAN-Port-Type 64 integer +ATTRIBUTE Aruba-Network-Test 65 integer +ATTRIBUTE Aruba-MPSK-Lookup-Info 66 string encrypt=1 +ATTRIBUTE Aruba-AVPair 67 string +ATTRIBUTE Aruba-DPP-Service-Type 68 integer + +ATTRIBUTE Aruba-User-Mgmt-Interface 69 string + +ATTRIBUTE Aruba-Poe-Allocate-By-Method 70 integer + +ATTRIBUTE Aruba-DPP-AKMs 71 string +ATTRIBUTE Aruba-DPP-Passphrase 72 string encrypt=2 VALUE Aruba-AirGroup-Device-Type Personal-Device 1 VALUE Aruba-AirGroup-Device-Type Shared-Device 2 @@ -98,4 +115,15 @@ VALUE Aruba-QoS-Trust-Mode None 2 VALUE Aruba-STP-Admin-Edge-Port Disable 0 VALUE Aruba-STP-Admin-Edge-Port Enable 1 +VALUE Aruba-PVLAN-Port-Type None 0 +VALUE Aruba-PVLAN-Port-Type Promiscuous 1 +VALUE Aruba-PVLAN-Port-Type Secondary 2 + +VALUE Aruba-DPP-Service-Type DDP-Boostrap-Authorization 1 +VALUE Aruba-DPP-Service-Type DDP-Identity-Update 2 +VALUE Aruba-DPP-Service-Type DDP-Net-Access 3 + +VALUE Aruba-PoE-Allocate-By-Method Class 1 +VALUE Aruba-PoE-Allocate-By-Method Usage 2 + END-VENDOR Aruba diff --git a/share/dictionary.calix b/share/dictionary.calix index 0a66326..4bcd1bf 100644 --- a/share/dictionary.calix +++ b/share/dictionary.calix @@ -12,6 +12,9 @@ VENDOR Calix 6321 +BEGIN-VENDOR Calix + +ATTRIBUTE Calix-Role 1 string ATTRIBUTE Calix-CMS-User-Group 220 string ATTRIBUTE Calix-CMS-Alarm-Filter 221 integer @@ -27,3 +30,5 @@ ATTRIBUTE Calix-CMS-Threshold-Event-Filter 223 integer VALUE Calix-CMS-Threshold-Event-Filter Disabled 0 VALUE Calix-CMS-Threshold-Event-Filter Enabled 1 + +END-VENDOR Calix
\ No newline at end of file diff --git a/share/dictionary.ckey b/share/dictionary.ckey new file mode 100644 index 0000000..5ff8533 --- /dev/null +++ b/share/dictionary.ckey @@ -0,0 +1,39 @@ +# -*- text -*- +# Copyright (C) 2019 The FreeRADIUS Server project and contributors +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# +############################################################################## +# +# $Id$ +# +############################################################################## + +VENDOR Ckey 61349 + +BEGIN-VENDOR Ckey + +ATTRIBUTE CKey-Auth-Info 10 integer + +VALUE CKey-Auth-Info Role-Reject 2 +VALUE CKey-Auth-Info Static-Reject 3 +VALUE CKey-Auth-Info Challenge-Dyna-Reject 7 +VALUE CKey-Auth-Info RADIUS-Key-Parse-Error 8 +VALUE CKey-Auth-Info Account-Does-Not-Exist 9 +VALUE CKey-Auth-Info Token-Expired 16 +VALUE CKey-Auth-Info Push-Check-Code-Error 20 +VALUE CKey-Auth-Info Push-Code-Error 21 +VALUE CKey-Auth-Info Single-Only-Dyna-Reject 22 +VALUE CKey-Auth-Info Static-PWD-Error 23 +VALUE CKey-Auth-Info PIN-Word-Error 24 +VALUE CKey-Auth-Info Source-IP-Error 25 +VALUE CKey-Auth-Info Msg-PWD-Reject 28 +VALUE CKey-Auth-Info Static-PWD-Expired 28 +VALUE CKey-Auth-Info Strategy-Config-Error 30 +VALUE CKey-Auth-Info EQP-Expiration-Error 31 +VALUE CKey-Auth-Info Account-EQP-Token-Mismatch 32 +VALUE CKey-Auth-Info Cannot-Login-Time 33 +VALUE CKey-Auth-Info Org-Reject 34 +VALUE CKey-Auth-Info Static-PWD-Or-PIN-Error 2324 +VALUE CKey-Auth-Info Unknown-Error 0xffffffff + +END-VENDOR Ckey diff --git a/share/dictionary.eleven b/share/dictionary.eleven index 93dabcb..de3921e 100644 --- a/share/dictionary.eleven +++ b/share/dictionary.eleven @@ -50,3 +50,4 @@ ATTRIBUTE Eleven-EAPOL-APMAC 4 octets # ATTRIBUTE Eleven-EAPOL-STMAC 5 octets +END-VENDOR Eleven diff --git a/share/dictionary.freeradius b/share/dictionary.freeradius index 0ac6182..2d7bd9c 100644 --- a/share/dictionary.freeradius +++ b/share/dictionary.freeradius @@ -187,6 +187,108 @@ ATTRIBUTE FreeRADIUS-Stats-Error 187 string ATTRIBUTE FreeRADIUS-Stats-Client-IPv6-Address 188 ipv6addr ATTRIBUTE FreeRADIUS-Stats-Server-IPv6-Address 189 ipv6addr +###################################################################### +# +# EAP-TEAP TLVs. Some are the same as EAP-FAST. some are unnecessarily different. +# +ATTRIBUTE FreeRADIUS-EAP-TEAP-TLV 190 tlv +ATTRIBUTE FreeRADIUS-EAP-TEAP-Authority-ID 190.1 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-Identity-Type 190.2 uint16 +VALUE FreeRADIUS-EAP-TEAP-Identity-Type User 1 +VALUE FreeRADIUS-EAP-TEAP-Identity-Type Machine 2 + +ATTRIBUTE FreeRADIUS-EAP-TEAP-Result 190.3 short +VALUE FreeRADIUS-EAP-TEAP-Result Success 1 +VALUE FreeRADIUS-EAP-TEAP-Result Failure 2 + +ATTRIBUTE FreeRADIUS-EAP-TEAP-NAK 190.4 octets # 4 octet Vendor-Id + 1 octet NAK type + TLVs +ATTRIBUTE FreeRADIUS-EAP-TEAP-Error 190.5 integer +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Expires-Soon 1 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Credential-Expires-Soon 2 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Authorizations-Change-Soon 3 +VALUE FreeRADIUS-EAP-TEAP-Error Clock-Skew-Detected 4 +VALUE FreeRADIUS-EAP-TEAP-Error Contact-Administrator 5 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Credentials-Change-Requires 6 +VALUE FreeRADIUS-EAP-TEAP-Error Inner-Method-Error 1001 +VALUE FreeRADIUS-EAP-TEAP-Error Unspecified-Authentication-Infrastructure-Problem 1002 +VALUE FreeRADIUS-EAP-TEAP-Error Unspecified-Authentication-Failure 1003 +VALUE FreeRADIUS-EAP-TEAP-Error Unspecified-Authorization-Failure 1004 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Credentials-Unavailable 1005 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Expired 1006 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Locked-Try-Again-Later 1007 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Locked-Admin-Intervention-Required 1008 +VALUE FreeRADIUS-EAP-TEAP-Error Authentication-Infrastructure-Unavailable 1009 +VALUE FreeRADIUS-EAP-TEAP-Error Authentication-Infrastructure-Not-Trusted 1010 +VALUE FreeRADIUS-EAP-TEAP-Error Clock-Skew-Too-Great 1011 +VALUE FreeRADIUS-EAP-TEAP-Error Invalid-Inner-Realm 1012 +VALUE FreeRADIUS-EAP-TEAP-Error Token-Out-of-Sync-Admin-Intervention-Required 1013 +VALUE FreeRADIUS-EAP-TEAP-Error Token-Out-of-Sync-PIN-Change-Required 1014 +VALUE FreeRADIUS-EAP-TEAP-Error Token-Revoked 1015 +VALUE FreeRADIUS-EAP-TEAP-Error Tokens-Exhausted 1016 +VALUE FreeRADIUS-EAP-TEAP-Error Challenge-Expired 1017 +VALUE FreeRADIUS-EAP-TEAP-Error Challenge-Algorithm-Mismatch 1018 +VALUE FreeRADIUS-EAP-TEAP-Error Client-Certificate-Not-Supplied 1019 +VALUE FreeRADIUS-EAP-TEAP-Error Client-Certificate-Rejected 1020 +VALUE FreeRADIUS-EAP-TEAP-Error Realm-Mismatch-Inner-Outer-Identity 1021 +VALUE FreeRADIUS-EAP-TEAP-Error Unsupported-Algorithm-in-CSR 1022 +VALUE FreeRADIUS-EAP-TEAP-Error Unsupported-Extension-in-CSR 1023 +VALUE FreeRADIUS-EAP-TEAP-Error Bad-Identity-in-CSR 1024 +VALUE FreeRADIUS-EAP-TEAP-Error Bad-CSR 1025 +VALUE FreeRADIUS-EAP-TEAP-Error Internal-CA-Error 1026 +VALUE FreeRADIUS-EAP-TEAP-Error General-PKI-Error 1027 +VALUE FreeRADIUS-EAP-TEAP-Error Inner-Method-Channel-Binding-Not-Supplied 1028 +VALUE FreeRADIUS-EAP-TEAP-Error Inner-Method-Channel-Binding-Missing-Info 1029 +VALUE FreeRADIUS-EAP-TEAP-Error Inner-Method-Channel-Binding-Data-Failed 1030 +VALUE FreeRADIUS-EAP-TEAP-Error User-Account-Credentials-Incorrect 1031 +VALUE FreeRADIUS-EAP-TEAP-Error Tunnel-Compromise-Error 2001 +VALUE FreeRADIUS-EAP-TEAP-Error Unexpected-TLVs 2002 + +ATTRIBUTE FreeRADIUS-EAP-TEAP-Channel-Binding 190.6 octets # complex format +ATTRIBUTE FreeRADIUS-EAP-TEAP-Vendor-Specific 190.7 octets # 4-octet vendor ID + TLVs +ATTRIBUTE FreeRADIUS-EAP-TEAP-Request-Action 190.8 octets # 1 octet + sub TLVs +ATTRIBUTE FreeRADIUS-EAP-TEAP-EAP-Payload 190.9 octets # EAP packet + TLVs + +ATTRIBUTE FreeRADIUS-EAP-TEAP-Intermediate-Result 190.10 short +VALUE FreeRADIUS-EAP-TEAP-Intermediate-Result Success 1 +VALUE FreeRADIUS-EAP-TEAP-Intermediate-Result Failure 2 + +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC 190.11 tlv +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Key 190.11.1 octets + +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Opaque 190.11.2 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Lifetime 190.11.3 integer +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-A-ID 190.11.4 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-I-ID 190.11.5 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-A-ID-Info 190.11.7 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Acknowledgement 190.11.8 short +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Info-TLV 190.11.9 tlv +ATTRIBUTE FreeRADIUS-EAP-TRAP-PAC-Type 186.11.10 short +# +# Sub-TLVs with the same numbers as the similar ones above. +# +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Info-PAC-Lifetime 190.11.9.3 integer +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Info-A-ID 190.11.9.4 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Info-I-ID 190.11.9.5 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Info-A-ID-Info 190.11.9.7 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Info-PAC-Type 190.11.9.10 short + +ATTRIBUTE FreeRADIUS-EAP-TEAP-PAC-Type 190.11.10 short + +ATTRIBUTE FreeRADIUS-EAP-TEAP-Crypto-Binding 190.12 octets # complex struct + +ATTRIBUTE FreeRADIUS-EAP-TEAP-Basic-Password-Auth-Req 190.13 string +ATTRIBUTE FreeRADIUS-EAP-TEAP-Basic-Password-Auth-Resp 190.14 octets # complex struct + +ATTRIBUTE FreeRADIUS-EAP-TEAP-PKCS7 190.15 octets +ATTRIBUTE FreeRADIUS-EAP-TEAP-PKCS10 190.16 octets + +ATTRIBUTE FreeRADIUS-EAP-TEAP-Trusted-Server-Root 190.17 octets # 1 octet + sub TLVs + +# +# End of EAP-TEAP +# +###################################################################### + # 190 is reserved ATTRIBUTE FreeRADIUS-Total-Auth-Conflicts 191 integer diff --git a/share/dictionary.freeradius.internal b/share/dictionary.freeradius.internal index bc6008c..52b9bb8 100644 --- a/share/dictionary.freeradius.internal +++ b/share/dictionary.freeradius.internal @@ -235,6 +235,10 @@ ATTRIBUTE MS-CHAP-New-NT-Password 1137 octets ATTRIBUTE Stripped-User-Domain 1138 string ATTRIBUTE Called-Station-SSID 1139 string +ATTRIBUTE Called-Station-MAC 1140 octets +ATTRIBUTE Pre-Shared-Key 1141 string +ATTRIBUTE Pairwise-Master-Key 1142 octets +ATTRIBUTE PSK-Identity 1143 string ATTRIBUTE OTP-Challenge 1145 string ATTRIBUTE EAP-Session-Id 1146 octets @@ -299,6 +303,8 @@ VALUE Proxy-Tunneled-Request-As-EAP No 0 VALUE Proxy-Tunneled-Request-As-EAP Yes 1 ATTRIBUTE Temp-Home-Server-String 1198 string +ATTRIBUTE TOTP-Time-Offset 1199 signed + # # Range: 1200-1279 # EAP-SIM (and other EAP type) weirdness. @@ -405,6 +411,8 @@ ATTRIBUTE EAP-Type-EAP-AKA2 1330 octets ATTRIBUTE EAP-Type-EAP-GPSK 1331 octets ATTRIBUTE EAP-Type-EAP-PWD 1332 octets ATTRIBUTE EAP-Type-EAP-EVEv1 1333 octets +ATTRIBUTE EAP-Type-EAP-PT-EAP 1334 octets +ATTRIBUTE EAP-Type-EAP-TEAP 1335 octets ATTRIBUTE EAP-Type-Microsoft-MS-CHAPv2 1306 octets ATTRIBUTE EAP-Type-Cisco-MS-CHAPv2 1309 octets @@ -651,7 +659,7 @@ ATTRIBUTE Radclient-Test-Name 2200 string # Free # # Range: 3000-3999 -# Site-local attributes (see raddb/dictionary.in) +# Site-local attributes (see raddb/dictionary) # Do NOT define attributes in this range! # # Range: 4000-65535 @@ -861,6 +869,8 @@ VALUE EAP-Type AKA2 50 VALUE EAP-Type GPSK 51 VALUE EAP-Type PWD 52 VALUE EAP-Type EKEv1 53 +VALUE EAP-Type PT-EAP 54 +VALUE EAP-Type TEAP 55 # # And this is what most people mean by MS-CHAPv2 diff --git a/share/dictionary.openwifi b/share/dictionary.openwifi new file mode 100644 index 0000000..794ad66 --- /dev/null +++ b/share/dictionary.openwifi @@ -0,0 +1,25 @@ +# -*- text -*- +# Copyright (C) 2019 The FreeRADIUS Server project and contributors +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# +############################################################################## +# +# OpenWiFi +# +# $Id$ +# +############################################################################## + +# +# Assigned to the Telecom Infra project, but used for OpenWifi +# +VENDOR OpenWiFi 58888 + +BEGIN-VENDOR OpenWiFi + +# +# As lowercase hex, with dashes between bytes. +# +ATTRIBUTE OpenWiFi-AP-MAC-Address 1 string + +END-VENDOR OpenWiFi diff --git a/share/dictionary.rfc8559 b/share/dictionary.rfc8559 index 4d3659c..196b19d 100644 --- a/share/dictionary.rfc8559 +++ b/share/dictionary.rfc8559 @@ -2,8 +2,8 @@ # Copyright (C) 2019 The FreeRADIUS Server project and contributors # This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 # -# Attributes and values defined in RFC 8859 -# http://www.ietf.org/rfc/rfc8859 +# Attributes and values defined in RFC 8559 +# http://www.ietf.org/rfc/rfc8559.txt # ATTRIBUTE Operator-NAS-Identifier 241.8 octets diff --git a/share/dictionary.ruckus b/share/dictionary.ruckus index 41e8959..882eb80 100644 --- a/share/dictionary.ruckus +++ b/share/dictionary.ruckus @@ -71,7 +71,10 @@ ATTRIBUTE Ruckus-Client-Host-Name 138 string ATTRIBUTE Ruckus-Client-Os-Type 139 string ATTRIBUTE Ruckus-Client-Os-Class 140 string ATTRIBUTE Ruckus-Vlan-Pool 141 string -ATTRIBUTE Ruckus-Dpsk 142 octets + +# 0x00 + (PBKDF2(HMAC−SHA1, passphrase, ssid, 4096, 256)) +# for a length of 33 octets. +ATTRIBUTE Ruckus-DPSK 142 octets ATTRIBUTE Ruckus-CP-Token 143 string ATTRIBUTE Ruckus-Max-DL-UL-Quota 144 integer ATTRIBUTE Ruckus-Traffic-Class-Attribute-Ids 145 string @@ -94,6 +97,10 @@ ATTRIBUTE Ruckus-DPSK-AKM-Suite 153.1 octets ATTRIBUTE Ruckus-DPSK-Cipher 153.2 byte ATTRIBUTE Ruckus-DPSK-Anonce 153.3 octets ATTRIBUTE Ruckus-DPSK-EAPOL-Key-Frame 153.4 octets +ATTRIBUTE Ruckus-Cluster-Name 154 string +ATTRIBUTE Ruckus-Domain-Name 155 string +ATTRIBUTE Ruckus-Client-Device-Type 156 string +ATTRIBUTE Ruckus-Vlan-Name 157 string # Ruckus SmartCell Insight Attributes ATTRIBUTE Ruckus-SCI-Role 200 string diff --git a/share/dictionary.smartsharesystems b/share/dictionary.smartsharesystems new file mode 100644 index 0000000..f955337 --- /dev/null +++ b/share/dictionary.smartsharesystems @@ -0,0 +1,33 @@ +# -*- text -*- +# SPDX-License-Identifier: CC-BY-4.0 +# Copyright (C) 2023 SmartShare Systems +# +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# +# Please contact SmartShare Systems directly if you need a copy under a different license. +# +# Version $Id$ + +############################################################################## +# +# SmartShare Systems Vendor-Specific Attributes +# dictionary.smartsharesystems +# https://www.smartsharesystems.com/ +# +############################################################################## + +VENDOR SmartShareSystems 30585 + +BEGIN-VENDOR SmartShareSystems + +# The globally unique identity (e.g. hardware serial number) of the Proxy (instance) forwarding the packet, +# in ASCII format (upper case only). +ATTRIBUTE SSS-Proxy-Id 2 string + +# The FQDN (Fully Qualified Domain Name) of the Proxy (cluster) forwarding the packet. +ATTRIBUTE SSS-Proxy-Identifier 3 string + +# Location of the Proxy (cluster) forwarding the packet, in free form UTF-8 format. +ATTRIBUTE SSS-Proxy-Location 4 string + +END-VENDOR SmartShareSystems diff --git a/share/dictionary.tplink b/share/dictionary.tplink new file mode 100644 index 0000000..f4a83a5 --- /dev/null +++ b/share/dictionary.tplink @@ -0,0 +1,19 @@ +# -*- text -*- +# Copyright (C) 2023 The FreeRADIUS Server project and contributors +# This work is licensed under CC-BY version 4.0 https://creativecommons.org/licenses/by/4.0 +# Version $Id$ +VENDOR TPLink 11863 + +BEGIN-VENDOR TPLink + +ATTRIBUTE TPLink-Recv-limit 1 integer +ATTRIBUTE TPLink-Xmit-limit 2 integer +ATTRIBUTE TPLink-Authentication-FindKey 3 octets +ATTRIBUTE TPLink-Authentication-FoundKey 4 octets +ATTRIBUTE TPLink-User-Command 5 string +ATTRIBUTE TPLink-Site 6 string +ATTRIBUTE TPLink-Omada 7 string +ATTRIBUTE TPLink-Redirect-Url 8 string +ATTRIBUTE TPLink-Portal-Access-Status 9 integer + +END-VENDOR TPLink diff --git a/share/dictionary.wispr b/share/dictionary.wispr index 42f3707..8ac0b83 100644 --- a/share/dictionary.wispr +++ b/share/dictionary.wispr @@ -37,5 +37,6 @@ ATTRIBUTE WBA-Financial-Clearing-Provider 13 string ATTRIBUTE WBA-Data-Clearing-Provider 14 string ATTRIBUTE WBA-Linear-Volume-Rate 15 octets ATTRIBUTE WBA-Identity-Provider 16 string +ATTRIBUTE WBA-Custom-SLA 17 string END-VENDOR WISPr diff --git a/src/include/clients.h b/src/include/clients.h index 46b5b3b..7e962b6 100644 --- a/src/include/clients.h +++ b/src/include/clients.h @@ -43,7 +43,11 @@ typedef struct radclient { char const *secret; //!< Secret PSK. - bool message_authenticator; //!< Require RADIUS message authenticator in requests. + fr_bool_auto_t require_ma; //!< Require RADIUS message authenticator in requests. + + bool dynamic_require_ma; //!< for dynamic clients + + fr_bool_auto_t limit_proxy_state; //!< Limit Proxy-State in requests char const *nas_type; //!< Type of client (arbitrary). diff --git a/src/include/conffile.h b/src/include/conffile.h index b996881..237469c 100644 --- a/src/include/conffile.h +++ b/src/include/conffile.h @@ -140,6 +140,7 @@ typedef struct timeval _timeval_t; #define PW_TYPE_MULTI (1 << 18) //!< CONF_PAIR can have multiple copies. #define PW_TYPE_NOT_EMPTY (1 << 19) //!< CONF_PAIR is required to have a non zero length value. #define PW_TYPE_FILE_EXISTS ((1 << 20) | PW_TYPE_STRING) //!< File matching value must exist +#define PW_TYPE_IGNORE_DEFAULT (1 << 21) //!< don't set from .dflt if the CONF_PAIR is missing /* @} **/ #define FR_INTEGER_COND_CHECK(_name, _var, _cond, _new)\ diff --git a/src/include/dlist.h b/src/include/dlist.h new file mode 100644 index 0000000..c1bc1d5 --- /dev/null +++ b/src/include/dlist.h @@ -0,0 +1,63 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/** + * $Id$ + * + * @file dlist.h + * @brief doubly linked lists + * + * @copyright 2023 Network RADIUS SAS (legal@networkradius.com) + */ + +#ifndef RADIUS_DLIST_H +#define RADIUS_DLIST_H + +RCSIDH(dlist_h, "$Id$") + +/* + * We have an internal cache, keyed by (mac + ssid). + * + * It returns the PMK and PSK for the user. + */ +typedef struct fr_dlist_s fr_dlist_t; + +struct fr_dlist_s { + fr_dlist_t *prev; + fr_dlist_t *next; +}; + +static inline void fr_dlist_entry_init(fr_dlist_t *entry) +{ + entry->prev = entry->next = entry; +} + +static inline CC_HINT(nonnull) void fr_dlist_entry_unlink(fr_dlist_t *entry) +{ + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + entry->prev = entry->next = entry; +} + +static inline CC_HINT(nonnull) void fr_dlist_insert_tail(fr_dlist_t *head, fr_dlist_t *entry) +{ + entry->next = head; + entry->prev = head->prev; + head->prev->next = entry; + head->prev = entry; +} + +#endif /* RADIUS_DLIST_H */ diff --git a/src/include/event.h b/src/include/event.h index 0409728..822da96 100644 --- a/src/include/event.h +++ b/src/include/event.h @@ -39,6 +39,8 @@ typedef void (*fr_event_fd_handler_t)(fr_event_list_t *el, int sock, void *ctx); fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status); +extern int fr_ev_max_fds; /* must be a power of 2 */ + int fr_event_list_num_fds(fr_event_list_t *el); int fr_event_list_num_elements(fr_event_list_t *el); diff --git a/src/include/features-h b/src/include/features-h index 158541f..1e2f29e 100644 --- a/src/include/features-h +++ b/src/include/features-h @@ -69,7 +69,7 @@ #ifdef WITH_TLS # ifdef WITH_COA # ifndef WITHOUT_COA_TUNNEL -# define WITH_COA_TUNNEL (1) +//# define WITH_COA_TUNNEL (1) # endif # endif #endif diff --git a/src/include/libradius.h b/src/include/libradius.h index 777927e..5cb5b06 100644 --- a/src/include/libradius.h +++ b/src/include/libradius.h @@ -410,6 +410,11 @@ typedef struct radius_packet { #ifdef WITH_RADIUSV11 bool radiusv11; #endif + bool tls; //!< uses secure transport + + bool message_authenticator; + bool proxy_state; + bool eap_message; } RADIUS_PACKET; typedef enum { @@ -527,6 +532,13 @@ DICT_VENDOR *dict_vendorbyvalue(int vendor); /* radius.c */ int rad_send(RADIUS_PACKET *, RADIUS_PACKET const *, char const *secret); bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason); + +/* + * 1 == require_ma + * 2 == msg_peek + * 4 == limit_proxy_state + * 8 == require_ma for Access-* replies and Protocol-Error + */ RADIUS_PACKET *rad_recv(TALLOC_CTX *ctx, int fd, int flags); ssize_t rad_recv_header(int sockfd, fr_ipaddr_t *src_ipaddr, uint16_t *src_port, int *code); void rad_recv_discard(int sockfd); @@ -720,7 +732,7 @@ extern bool fr_dns_lookups; /* do IP -> hostname lookups? */ extern bool fr_hostname_lookups; /* do hostname -> IP lookups? */ extern int fr_debug_lvl; /* 0 = no debugging information */ extern uint32_t fr_max_attributes; /* per incoming packet */ -#define FR_MAX_PACKET_CODE (52) +#define FR_MAX_PACKET_CODE (53) extern char const *fr_packet_codes[FR_MAX_PACKET_CODE]; #define is_radius_code(_x) ((_x > 0) && (_x < FR_MAX_PACKET_CODE)) extern FILE *fr_log_fp; @@ -958,6 +970,12 @@ int fr_socket_wait_for_connect(int sockfd, struct timeval *timeout); } #endif +typedef enum { + FR_BOOL_FALSE = 0, + FR_BOOL_TRUE, + FR_BOOL_AUTO, +} fr_bool_auto_t; + #include <freeradius-devel/packet.h> #ifdef WITH_TCP diff --git a/src/include/radius.h b/src/include/radius.h index 473528d..147d674 100644 --- a/src/include/radius.h +++ b/src/include/radius.h @@ -61,6 +61,7 @@ typedef enum { PW_CODE_COA_REQUEST = 43, //!< RFC3575/RFC5176 - CoA-Request PW_CODE_COA_ACK = 44, //!< RFC3575/RFC5176 - CoA-Ack (positive) PW_CODE_COA_NAK = 45, //!< RFC3575/RFC5176 - CoA-Nak (not willing to perform) + PW_CODE_PROTOCOL_ERROR = 52, //!< RFC7930 - Protocol layer issue PW_CODE_MAX = 255, //!< Maximum possible code } PW_CODE; diff --git a/src/include/radiusd.h b/src/include/radiusd.h index 594a6bd..c7c03cd 100644 --- a/src/include/radiusd.h +++ b/src/include/radiusd.h @@ -141,6 +141,8 @@ typedef struct main_config { uint32_t cleanup_delay; //!< How long before cleaning up cached responses. uint32_t max_requests; + uint32_t proxy_dedup_window; //!< suppress duplicate retransmitssions from a NAS + bool postauth_client_lost; //!< Whether to run Post-Auth-Type Client-Lost section uint32_t debug_level; @@ -174,6 +176,9 @@ typedef struct main_config { bool exiting; //!< are we exiting? + fr_bool_auto_t require_ma; //!< global configuration for all clients and home servers + + fr_bool_auto_t limit_proxy_state; //!< global configuration for all clients #ifdef ENABLE_OPENSSL_VERSION_CHECK char const *allow_vulnerable_openssl; //!< The CVE number of the last security issue acknowledged. @@ -194,9 +199,8 @@ typedef struct main_config { typedef enum { REQUEST_ACTIVE = 1, REQUEST_STOP_PROCESSING, - REQUEST_COUNTED } rad_master_state_t; -#define REQUEST_MASTER_NUM_STATES (REQUEST_COUNTED + 1) +#define REQUEST_MASTER_NUM_STATES (REQUEST_STOP_PROCESSING + 1) typedef enum { REQUEST_QUEUED = 1, @@ -320,6 +324,7 @@ struct rad_request { #define RAD_REQUEST_OPTION_COA (1 << 0) #define RAD_REQUEST_OPTION_CTX (1 << 1) #define RAD_REQUEST_OPTION_CANCELLED (1 << 2) +#define RAD_REQUEST_OPTION_STATS (1 << 3) #define SECONDS_PER_DAY 86400 #define MAX_REQUEST_TIME 30 @@ -565,6 +570,8 @@ int main_config_free(void); void main_config_hup(void); void hup_logfile(void); +int fr_bool_auto_parse(CONF_PAIR *cp, fr_bool_auto_t *out, char const *str); + /* listen.c */ void listen_free(rad_listen_t **head); int listen_init(CONF_SECTION *cs, rad_listen_t **head, bool spawn_flag); diff --git a/src/include/realms.h b/src/include/realms.h index 23806f4..cc5d4c1 100644 --- a/src/include/realms.h +++ b/src/include/realms.h @@ -58,6 +58,8 @@ typedef struct fr_socket_limit_t { uint32_t num_requests; uint32_t lifetime; uint32_t idle_timeout; + uint32_t read_timeout; + uint32_t write_timeout; } fr_socket_limit_t; typedef struct home_server { @@ -69,6 +71,8 @@ typedef struct home_server { bool dual; //!< One of a pair of homeservers on consecutive ports. bool dynamic; //!< is this a dynamically added home server? bool nonblock; //!< Enable a socket non-blocking to the home server. + fr_bool_auto_t require_ma; //!< for all replies to Access-Request and Status-Server + #ifdef WITH_COA_TUNNEL bool recv_coa; //!< receive CoA packets, too #endif diff --git a/src/include/tls-h b/src/include/tls-h index 4bf1665..506fb19 100644 --- a/src/include/tls-h +++ b/src/include/tls-h @@ -152,6 +152,9 @@ typedef struct _tls_session_t { //!< If set to no then only the first fragment contains length. int peap_flag; + VALUE_PAIR *outer_tlvs; //!< only for TEAP, and only for the first fragment. + uint8_t *outer_tlvs_octets; //!< only for TEAP, needed for Crypto-Binding TLV + size_t tls_record_in_total_len; //!< How long the peer indicated the complete tls record //!< would be. size_t tls_record_in_recvd_len; //!< How much of the record we've received so far. @@ -176,17 +179,19 @@ typedef struct _tls_session_t { * * 0 1 2 3 4 5 6 7 8 * +-+-+-+-+-+-+-+-+ - * |L M S R R R R R| + * |L M S O R R R R| * +-+-+-+-+-+-+-+-+ * * L = Length included * M = More fragments * S = EAP-TLS start + * O = outer TLV length included (4 octets, only for TEAP) * R = Reserved */ #define TLS_START(x) (((x) & 0x20) != 0) #define TLS_MORE_FRAGMENTS(x) (((x) & 0x40) != 0) #define TLS_LENGTH_INCLUDED(x) (((x) & 0x80) != 0) +#define TLS_OUTER_TLV_INCLUDED(x) (((x) & 0x10) != 0) #define TLS_CHANGE_CIPHER_SPEC(x) (((x) & 0x0014) == 0x0014) #define TLS_ALERT(x) (((x) & 0x0015) == 0x0015) @@ -195,6 +200,7 @@ typedef struct _tls_session_t { #define SET_START(x) ((x) | (0x20)) #define SET_MORE_FRAGMENTS(x) ((x) | (0x40)) #define SET_LENGTH_INCLUDED(x) ((x) | (0x80)) +#define SET_OUTER_TLV_INCLUDED(x) ((x) | (0x10)) /* * Following enums from rfc2246 @@ -351,6 +357,8 @@ struct fr_tls_server_conf_t { SSL_CTX *ctx; CONF_SECTION *cs; + char const *name; //!< name of the thing doing TLS. + char const *private_key_password; char const *private_key_file; char const *certificate_file; diff --git a/src/lib/event.c b/src/lib/event.c index 9eb9d1a..0d926ad 100644 --- a/src/lib/event.c +++ b/src/lib/event.c @@ -47,7 +47,9 @@ typedef struct fr_event_fd_t { void *ctx; } fr_event_fd_t; -#define FR_EV_MAX_FDS (512) +#define FR_EV_MAX_EVENTS (512) + +int fr_ev_max_fds = FR_EV_MAX_EVENTS; #undef USEC #define USEC (1000000) @@ -71,10 +73,10 @@ struct fr_event_list_t { fd_set write_fds; #else int kq; - struct kevent events[FR_EV_MAX_FDS]; /* so it doesn't go on the stack every time */ + struct kevent events[FR_EV_MAX_EVENTS]; /* so it doesn't go on the stack every time */ #endif - fr_event_fd_t readers[FR_EV_MAX_FDS]; + fr_event_fd_t readers[1]; }; /* @@ -128,11 +130,12 @@ fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status) int i; fr_event_list_t *el; - el = talloc_zero(ctx, fr_event_list_t); + el = (fr_event_list_t *) talloc_zero_array(ctx, uint8_t, sizeof(fr_event_list_t) + fr_ev_max_fds * sizeof(el->readers[0])); if (!fr_assert(el)) { return NULL; } talloc_set_destructor(el, _event_list_free); + talloc_set_type(el, fr_event_list_t); el->times = fr_heap_create(fr_event_list_time_cmp, offsetof(fr_event_t, heap)); if (!el->times) { @@ -140,7 +143,7 @@ fr_event_list_t *fr_event_list_create(TALLOC_CTX *ctx, fr_event_status_t status) return NULL; } - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { el->readers[i].fd = -1; } @@ -363,7 +366,7 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd, return 0; } - if (el->num_readers >= FR_EV_MAX_FDS) { + if (el->num_readers >= fr_ev_max_fds) { fr_strerror_printf("Too many readers"); return 0; } @@ -385,11 +388,11 @@ int fr_event_fd_insert(fr_event_list_t *el, int type, int fd, * usually less than 256, we do "FD & 0xff", which is a * good guess, and makes the lookups mostly O(1). */ - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { int j; struct kevent evset; - j = (i + fd) & (FR_EV_MAX_FDS - 1); + j = (i + fd) & (fr_ev_max_fds - 1); if (el->readers[j].fd >= 0) continue; @@ -469,11 +472,11 @@ int fr_event_fd_write_handler(fr_event_list_t *el, int type, int fd, if (type != 0) return 0; #ifdef HAVE_KQUEUE - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { int j; struct kevent evset; - j = (i + fd) & (FR_EV_MAX_FDS - 1); + j = (i + fd) & (fr_ev_max_fds - 1); if (el->readers[j].fd != fd) continue; @@ -528,11 +531,11 @@ int fr_event_fd_delete(fr_event_list_t *el, int type, int fd) if (type != 0) return 0; #ifdef HAVE_KQUEUE - for (i = 0; i < FR_EV_MAX_FDS; i++) { + for (i = 0; i < fr_ev_max_fds; i++) { int j; struct kevent evset; - j = (i + fd) & (FR_EV_MAX_FDS - 1); + j = (i + fd) & (fr_ev_max_fds - 1); if (el->readers[j].fd != fd) continue; @@ -676,7 +679,7 @@ int fr_event_loop(fr_event_list_t *el) ts_wake = NULL; } - rcode = kevent(el->kq, NULL, 0, el->events, FR_EV_MAX_FDS, ts_wake); + rcode = kevent(el->kq, NULL, 0, el->events, FR_EV_MAX_EVENTS, ts_wake); #endif /* HAVE_KQUEUE */ if (fr_heap_num_elements(el->times) > 0) { diff --git a/src/lib/packet.c b/src/lib/packet.c index 971980b..0f870f5 100644 --- a/src/lib/packet.c +++ b/src/lib/packet.c @@ -723,6 +723,8 @@ bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, */ id = fd = -1; + if (request->id >= 0 && request->id < 256) + id = request->id; start_i = fr_rand() & SOCKOFFSET_MASK; #define ID_i ((i + start_i) & SOCKOFFSET_MASK) @@ -832,6 +834,18 @@ bool fr_packet_list_id_alloc(fr_packet_list_t *pl, int proto, */ /* + * An explicit ID was requested + */ + + if (id != -1) { + if ((ps->id[(id >> 3) & 0x1f] & (1 << (id & 0x07))) != 0) continue; + + ps->id[(id >> 3) & 0x1f] |= (1 << (id & 0x07)); + fd = i; + break; + } + + /* * Look for a free Id, starting from a random number. */ start_j = fr_rand() & 0x1f; @@ -1076,7 +1090,7 @@ void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received) * This really belongs in a utility library */ if (is_radius_code(packet->code)) { - fprintf(fp, "%s %s Id %i from %s%s%s:%x to %s%s%s:%u length %zu\n", + fprintf(fp, "%s %s Id %i from %s%s%s:%u to %s%s%s:%u length %zu\n", received ? "Received" : "Sent", fr_packet_codes[packet->code], packet->id, @@ -1094,7 +1108,7 @@ void fr_packet_header_print(FILE *fp, RADIUS_PACKET *packet, bool received) packet->dst_port, packet->data_len); } else { - fprintf(fp, "%s code %u Id %i from %s%s%s:%u to %s%s%s:%i length %zu\n", + fprintf(fp, "%s code %u Id %i from %s%s%s:%u to %s%s%s:%u length %zu\n", received ? "Received" : "Sent", packet->code, packet->id, diff --git a/src/lib/print.c b/src/lib/print.c index 57455b6..83aa267 100644 --- a/src/lib/print.c +++ b/src/lib/print.c @@ -529,6 +529,7 @@ size_t vp_prints_value_json(char *out, size_t outlen, VALUE_PAIR const *vp, bool switch (vp->da->type) { case PW_TYPE_STRING: + case PW_TYPE_OCTETS: for (q = vp->vp_strvalue; q < vp->vp_strvalue + vp->vp_length; q++) { /* Indicate truncation */ if (freespace < 3) return outlen + 1; diff --git a/src/lib/radius.c b/src/lib/radius.c index b2de15b..a19c9a4 100644 --- a/src/lib/radius.c +++ b/src/lib/radius.c @@ -145,8 +145,9 @@ char const *fr_packet_codes[FR_MAX_PACKET_CODE] = { "47", "48", "49", - "IP-Address-Allocate", - "IP-Address-Release", //!< 50 + "IP-Address-Allocate", //!< 50 + "IP-Address-Release", + "Protocol-Error", }; @@ -536,7 +537,7 @@ static void make_secret(uint8_t *digest, uint8_t const *vector, fr_md5_destroy(&context); } -#define MAX_PASS_LEN (128) +#define MAX_PASS_LEN (256) static void make_passwd(uint8_t *output, ssize_t *outlen, uint8_t const *input, size_t inlen, char const *secret, uint8_t const *vector) @@ -1819,6 +1820,14 @@ int rad_vp2attr(RADIUS_PACKET const *packet, RADIUS_PACKET const *original, return rad_vp2vsa(packet, original, secret, pvp, start, room); } +static const bool code2ma[FR_MAX_PACKET_CODE] = { + [ PW_CODE_ACCESS_REQUEST ] = true, + [ PW_CODE_ACCESS_ACCEPT ] = true, + [ PW_CODE_ACCESS_REJECT ] = true, + [ PW_CODE_ACCESS_CHALLENGE ] = true, + [ PW_CODE_STATUS_SERVER ] = true, + [ PW_CODE_PROTOCOL_ERROR ] = true, +}; /** Encode a packet * @@ -1831,6 +1840,7 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, uint16_t total_length; int len; VALUE_PAIR const *reply; + bool seen_ma = false; /* * A 4K packet, aligned on 64-bits. @@ -1883,6 +1893,12 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, id = htonl(id); memcpy(hdr->vector, &id, sizeof(id)); memset(hdr->vector + sizeof(id), 0, sizeof(hdr->vector) - sizeof(id)); + + /* + * We don't encode Message-Authenticator + */ + seen_ma = true; + packet->offset = -1; } else #endif { @@ -1909,6 +1925,27 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, */ /* + * Always add Message-Authenticator for replies to + * Access-Request packets, and for all Access-Accept, + * Access-Reject, Access-Challenge. + * + * It must be the FIRST attribute in the packet. + */ + if (!packet->tls && + ((code2ma[packet->code]) || (original && code2ma[original->code]))) { + seen_ma = true; + + packet->offset = RADIUS_HDR_LEN; + + ptr[0] = PW_MESSAGE_AUTHENTICATOR; + ptr[1] = 18; + memset(ptr + 2, 0, 16); + + ptr += 18; + total_length += 18; + } + + /* * Loop over the reply attributes for the packet. */ reply = packet->vps; @@ -1943,18 +1980,9 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, #ifdef WITH_RADIUSV11 /* - * Do not encode Message-Authenticator for RADIUS/1.1 - */ - if ((reply->da->vendor == 0) && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { - reply = reply->next; - continue; - } - - - /* * Do not encode Original-Packet-Code for RADIUS/1.1 */ - if (reply->da->vendor == ((unsigned int) PW_EXTENDED_ATTRIBUTE_1 << 24) && (reply->da->attr == 4)) { + if (packet->radiusv11 && reply->da->vendor == ((unsigned int) PW_EXTENDED_ATTRIBUTE_1 << 24) && (reply->da->attr == 4)) { reply = reply->next; continue; } @@ -1984,15 +2012,13 @@ int rad_encode(RADIUS_PACKET *packet, RADIUS_PACKET const *original, * length and initial value. */ if (!reply->da->vendor && (reply->da->attr == PW_MESSAGE_AUTHENTICATOR)) { -#ifdef WITH_RADIUSV11 /* - * RADIUSV11 does not encode or verify Message-Authenticator. + * We have already encoded the Message-Authenticator, don't do it again. */ - if (packet->radiusv11) { + if (seen_ma) { reply = reply->next; continue; } -#endif if (room < 18) break; @@ -2152,11 +2178,7 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original, case PW_CODE_ACCOUNTING_REQUEST: case PW_CODE_DISCONNECT_REQUEST: - case PW_CODE_DISCONNECT_ACK: - case PW_CODE_DISCONNECT_NAK: case PW_CODE_COA_REQUEST: - case PW_CODE_COA_ACK: - case PW_CODE_COA_NAK: memset(hdr->vector, 0, AUTH_VECTOR_LEN); break; @@ -2164,6 +2186,10 @@ int rad_sign(RADIUS_PACKET *packet, RADIUS_PACKET const *original, case PW_CODE_ACCESS_ACCEPT: case PW_CODE_ACCESS_REJECT: case PW_CODE_ACCESS_CHALLENGE: + case PW_CODE_DISCONNECT_ACK: + case PW_CODE_DISCONNECT_NAK: + case PW_CODE_COA_ACK: + case PW_CODE_COA_NAK: memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN); break; @@ -2510,6 +2536,8 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) char host_ipaddr[128]; #ifndef WITH_RADIUSV11_ONLY bool require_ma = false; + bool limit_proxy_state = false; + bool seen_proxy_state = false; bool seen_ma = false; bool eap = false; bool non_eap = false; @@ -2559,15 +2587,23 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) } /* - * Message-Authenticator is required in Status-Server - * packets, otherwise they can be trivially forged. + * If the caller requires Message-Authenticator, then set + * the flag. + * + * We also require Message-Authenticator if the packet + * code is Status-Server. + * + * If we're receiving packets from a proxy socket, then + * require Message-Authenticator for Access-* replies, + * and for Protocol-Error. */ - if (hdr->code == PW_CODE_STATUS_SERVER) require_ma = true; + require_ma = ((flags & 0x01) != 0) || (hdr->code == PW_CODE_STATUS_SERVER) || (((flags & 0x08) != 0) && code2ma[hdr->code]); /* - * It's also required if the caller asks for it. + * We only limit Proxy-State if we're not requiring + * Message-Authenticator. */ - if (flags) require_ma = true; + limit_proxy_state = ((flags & 0x04) != 0) && !require_ma; /* * Repeat the length checks. This time, instead of @@ -2723,6 +2759,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) case PW_EAP_MESSAGE: require_ma = true; eap = true; + packet->eap_message = true; break; case PW_USER_PASSWORD: @@ -2731,6 +2768,11 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) non_eap = true; break; + case PW_PROXY_STATE: + seen_proxy_state = true; + packet->proxy_state = true; + break; + case PW_MESSAGE_AUTHENTICATOR: #ifdef WITH_RADIUSV11 /* @@ -2749,6 +2791,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) goto finish; } seen_ma = true; + packet->message_authenticator = true; break; } #endif @@ -2813,7 +2856,7 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) !packet->radiusv11 && #endif !seen_ma) { - FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute", + FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute. You may need to set \"require_message_authenticator = no\" in the configuration.", inet_ntop(packet->src_ipaddr.af, &packet->src_ipaddr.ipaddr, host_ipaddr, sizeof(host_ipaddr))); @@ -2822,6 +2865,18 @@ bool rad_packet_ok(RADIUS_PACKET *packet, int flags, decode_fail_t *reason) } #ifndef WITH_RADIUSV11_ONLY + /* + * The client is a NAS which shouldn't send Proxy-State, but it did! + */ + if (limit_proxy_state && seen_proxy_state && !seen_ma) { + FR_DEBUG_STRERROR_PRINTF("Insecure packet from host %s: Packet does not contain required Message-Authenticator attribute, but still has one or more Proxy-State attributes", + inet_ntop(packet->src_ipaddr.af, + &packet->src_ipaddr.ipaddr, + host_ipaddr, sizeof(host_ipaddr))); + failure = DECODE_FAIL_MA_MISSING; + goto finish; + } + if (eap && non_eap) { FR_DEBUG_STRERROR_PRINTF("Bad packet from host %s: Packet contains EAP-Message and non-EAP authentication attribute", inet_ntop(packet->src_ipaddr.af, @@ -3938,7 +3993,7 @@ ssize_t data2vp(TALLOC_CTX *ctx, VALUE_PAIR *vp; uint8_t const *data = start; char *p; - uint8_t buffer[256]; + uint8_t buffer[MAX_PASS_LEN]; /* * FIXME: Attrlen can be larger than 253 for extended attrs! @@ -4054,7 +4109,7 @@ ssize_t data2vp(TALLOC_CTX *ctx, attrlen, secret, packet->vector); } - buffer[253] = '\0'; + buffer[attrlen] = '\0'; /* * MS-CHAP-MPPE-Keys are 24 octets, and @@ -4654,22 +4709,24 @@ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, ssize_t my_len; #ifdef WITH_RADIUSV11 - /* - * Don't decode Message-Authenticator - */ - if (ptr[0] == PW_MESSAGE_AUTHENTICATOR) { - packet_length -= ptr[1]; - ptr += ptr[1]; - continue; - } + if (packet->radiusv11) { + /* + * Don't decode Message-Authenticator + */ + if (ptr[0] == PW_MESSAGE_AUTHENTICATOR) { + packet_length -= ptr[1]; + ptr += ptr[1]; + continue; + } - /* - * Don't decode Original-Packet-Code - */ - if ((ptr[0] == PW_EXTENDED_ATTRIBUTE_1) && (ptr[1] >= 3) && (ptr[2] == 4)) { - packet_length -= ptr[1]; - ptr += ptr[1]; - continue; + /* + * Don't decode Original-Packet-Code + */ + if ((ptr[0] == PW_EXTENDED_ATTRIBUTE_1) && (ptr[1] >= 3) && (ptr[2] == 4)) { + packet_length -= ptr[1]; + ptr += ptr[1]; + continue; + } } #endif @@ -4761,7 +4818,7 @@ int rad_pwencode(char *passwd, size_t *pwlen, char const *secret, */ len = *pwlen; - if (len > 128) len = 128; + if (len > MAX_STRING_LEN) len = MAX_STRING_LEN; if (len == 0) { memset(passwd, 0, AUTH_PASS_LEN); @@ -4821,13 +4878,6 @@ int rad_pwdecode(char *passwd, size_t pwlen, char const *secret, size_t n, secretlen; /* - * The RFC's say that the maximum is 128. - * The buffer we're putting it into above is 254, so - * we don't need to do any length checking. - */ - if (pwlen > 128) pwlen = 128; - - /* * Catch idiots. */ if (pwlen == 0) goto done; diff --git a/src/main/all.mk b/src/main/all.mk index 2517cd2..f3db386 100644 --- a/src/main/all.mk +++ b/src/main/all.mk @@ -1,3 +1,3 @@ SUBMAKEFILES := radclient.mk radiusd.mk radsniff.mk radmin.mk radattr.mk \ - radwho.mk radlast.mk radtest.mk radzap.mk checkrad.mk \ + radwho.mk radlast.mk radtest.mk radzap.mk checkrad.mk radsecret.mk \ libfreeradius-server.mk unittest.mk diff --git a/src/main/auth.c b/src/main/auth.c index 84889b8..2dc3e60 100644 --- a/src/main/auth.c +++ b/src/main/auth.c @@ -850,8 +850,8 @@ int rad_virtual_server(REQUEST *request) break; case PW_AUTH_TYPE_REJECT: - request->reply->code = PW_CODE_ACCESS_REJECT; - break; + request->reply->code = PW_CODE_ACCESS_REJECT; + break; default: break; @@ -864,6 +864,12 @@ int rad_virtual_server(REQUEST *request) if (vp) rad_postauth(request); } + if (request->reply->code == PW_CODE_ACCESS_CHALLENGE) { + fr_pair_delete_by_num(&request->config, PW_POST_AUTH_TYPE, 0, TAG_ANY); + vp = pair_make_config("Post-Auth-Type", "Challenge", T_OP_SET); + if (vp) rad_postauth(request); + } + if (request->reply->code == PW_CODE_ACCESS_ACCEPT) { /* * Check that there is a name which can be used diff --git a/src/main/cb.c b/src/main/cb.c index db764aa..65e484f 100644 --- a/src/main/cb.c +++ b/src/main/cb.c @@ -31,6 +31,7 @@ void cbtls_info(SSL const *s, int where, int ret) { char const *role, *state; REQUEST *request = SSL_get_ex_data(s, FR_TLS_EX_INDEX_REQUEST); + fr_tls_server_conf_t *conf = (fr_tls_server_conf_t *) SSL_get_ex_data(s, FR_TLS_EX_INDEX_CONF); if ((where & ~SSL_ST_MASK) & SSL_ST_CONNECT) { role = "Client "; @@ -58,7 +59,7 @@ void cbtls_info(SSL const *s, int where, int ret) len = strlen(abbrv); if ((len > 1) && (abbrv[len - 1] == ' ')) len--; - RDEBUG3("(TLS) Handshake state [%.*s] - %s%s (%d)", + RDEBUG3("(TLS) %s - Handshake state [%.*s] - %s%s (%d)", conf->name, (int)len, abbrv, role, state, SSL_get_state(s)); /* @@ -82,7 +83,7 @@ void cbtls_info(SSL const *s, int where, int ret) client_ciphers = SSL_get_client_ciphers(s); if (client_ciphers) { - RDEBUG3("Client preferred ciphers (by priority)"); + RDEBUG3("(TLS) %s - Client preferred ciphers (by priority)", conf->name); num_ciphers = sk_SSL_CIPHER_num(client_ciphers); for (i = 0; i < num_ciphers; i++) { this_cipher = sk_SSL_CIPHER_value(client_ciphers, i); @@ -92,7 +93,7 @@ void cbtls_info(SSL const *s, int where, int ret) } #endif } else { - RDEBUG2("(TLS) Handshake state - %s%s", role, state); + RDEBUG2("(TLS) %s - Handshake state - %s%s", conf->name, role, state); } return; } @@ -100,23 +101,27 @@ void cbtls_info(SSL const *s, int where, int ret) if (where & SSL_CB_ALERT) { if ((ret & 0xff) == SSL_AD_CLOSE_NOTIFY) return; - RERROR("(TLS) Alert %s:%s:%s", (where & SSL_CB_READ) ? "read": "write", + RERROR("(TLS) %s - Alert %s:%s:%s", conf->name, (where & SSL_CB_READ) ? "read": "write", SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); return; } if (where & SSL_CB_EXIT) { if (ret == 0) { - RERROR("(TLS) %s: Failed in %s", role, state); + RERROR("(TLS) %s - %s: Failed in %s", conf->name, role, state); return; } if (ret < 0) { if (SSL_want_read(s)) { - RDEBUG2("(TLS) %s: Need to read more data: %s", role, state); + RDEBUG2("(TLS) %s - %s: Need to read more data: %s", conf->name, role, state); return; } - RERROR("(TLS) %s: Error in %s", role, state); + if (SSL_want_write(s)) { + RDEBUG2("(TLS) %s - %s: Need to write more data: %s", conf->name, role, state); + return; + } + RERROR("(TLS) %s - %s: Error in %s", conf->name, role, state); } } } diff --git a/src/main/client.c b/src/main/client.c index 12f7824..58f9faa 100644 --- a/src/main/client.c +++ b/src/main/client.c @@ -328,7 +328,8 @@ check_list: (old->coa_home_server == client->coa_home_server) && (old->coa_home_pool == client->coa_home_pool) && #endif - (old->message_authenticator == client->message_authenticator)) { + (old->require_ma == client->require_ma) && + (old->limit_proxy_state == client->limit_proxy_state)) { WARN("Ignoring duplicate client %s", client->longname); client_free(client); return true; @@ -490,6 +491,8 @@ static fr_ipaddr_t cl_ipaddr; static uint32_t cl_netmask; static char const *cl_srcipaddr = NULL; static char const *hs_proto = NULL; +static char const *require_message_authenticator = NULL; +static char const *limit_proxy_state = NULL; #ifdef WITH_TCP static CONF_PARSER limit_config[] = { @@ -512,7 +515,8 @@ static const CONF_PARSER client_config[] = { { "src_ipaddr", FR_CONF_POINTER(PW_TYPE_STRING, &cl_srcipaddr), NULL }, - { "require_message_authenticator", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), "no" }, + { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL }, + { "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &limit_proxy_state), NULL }, { "secret", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, RADCLIENT, secret), NULL }, { "shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), NULL }, @@ -724,7 +728,7 @@ static const CONF_PARSER dynamic_config[] = { { "FreeRADIUS-Client-Src-IP-Address", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, RADCLIENT, src_ipaddr), NULL }, { "FreeRADIUS-Client-Src-IPv6-Address", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, RADCLIENT, src_ipaddr), NULL }, - { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, message_authenticator), NULL }, + { "FreeRADIUS-Client-Require-MA", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, RADCLIENT, dynamic_require_ma), NULL }, { "FreeRADIUS-Client-Secret", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, secret), "" }, { "FreeRADIUS-Client-Shortname", FR_CONF_OFFSET(PW_TYPE_STRING, RADCLIENT, shortname), "" }, @@ -906,8 +910,19 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo c = talloc_zero(ctx, RADCLIENT); c->cs = cs; + /* + * Set the "require message authenticator" and "limit + * proxy state" flags from the global default. If the + * configuration item exists, AND is set, it will + * over-ride the flag. + */ + c->require_ma = main_config.require_ma; + c->limit_proxy_state = main_config.limit_proxy_state; + memset(&cl_ipaddr, 0, sizeof(cl_ipaddr)); cl_netmask = 255; + require_message_authenticator = NULL; + limit_proxy_state = NULL; if (cf_section_parse(cs, c, client_config) < 0) { cf_log_err_cs(cs, "Error parsing client section"); @@ -917,6 +932,8 @@ RADCLIENT *client_afrom_cs(TALLOC_CTX *ctx, CONF_SECTION *cs, bool in_server, bo hs_proto = NULL; cl_srcipaddr = NULL; #endif + require_message_authenticator = NULL; + limit_proxy_state = NULL; return NULL; } @@ -1189,6 +1206,16 @@ done_coa: } #endif + if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &c->require_ma, require_message_authenticator) < 0) { + goto error; + } + + if (c->require_ma != FR_BOOL_TRUE) { + if (fr_bool_auto_parse(cf_pair_find(cs, "limit_proxy_state"), &c->limit_proxy_state, limit_proxy_state) < 0) { + goto error; + } + } + return c; } @@ -1233,7 +1260,7 @@ RADCLIENT *client_afrom_query(TALLOC_CTX *ctx, char const *identifier, char cons if (shortname) c->shortname = talloc_typed_strdup(c, shortname); if (type) c->nas_type = talloc_typed_strdup(c, type); if (server) c->server = talloc_typed_strdup(c, server); - c->message_authenticator = require_ma; + c->require_ma = require_ma; return c; } @@ -1419,10 +1446,10 @@ RADCLIENT *client_afrom_request(RADCLIENT_LIST *clients, REQUEST *request) *pi = vp->vp_integer; /* - * Same nastiness as above. + * Same nastiness as above, but hard-coded for require Message-Authenticator. */ for (parse = client_config; parse->name; parse++) { - if (parse->offset == dynamic_config[i].offset) break; + if (parse->type == PW_TYPE_BOOLEAN) break; } if (!parse) break; @@ -1513,6 +1540,11 @@ validate: goto error; } + /* + * It can't be set to "auto". Too bad. + */ + c->require_ma = (fr_bool_auto_t) c->dynamic_require_ma; + if (!client_add_dynamic(clients, request->client, c)) { return NULL; } diff --git a/src/main/command.c b/src/main/command.c index 988f43b..266366b 100644 --- a/src/main/command.c +++ b/src/main/command.c @@ -2701,7 +2701,7 @@ static int command_stats_socket(rad_listen_t *listener, int argc, char *argv[]) return command_print_stats(listener, &sock->stats, auth, 0); } -static int command_stats_pool(rad_listen_t *listener, UNUSED int argc, UNUSED char *argv[]) +static int command_stats_pool(rad_listen_t *listener, int argc, char *argv[]) { CONF_SECTION *cs; module_instance_t *mi; diff --git a/src/main/conffile.c b/src/main/conffile.c index 7bb206c..ad5a5fe 100644 --- a/src/main/conffile.c +++ b/src/main/conffile.c @@ -1424,12 +1424,13 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d { int rcode; bool deprecated, required, attribute, secret, file_input, cant_be_empty, tmpl, multi, file_exists; + bool ignore_dflt; char **q; char const *value; CONF_PAIR *cp = NULL; fr_ipaddr_t *ipaddr; - char buffer[8192]; CONF_ITEM *c_item; + char buffer[8192]; if (!cs) { cf_log_err(&(cs->item), "No enclosing section for configuration item \"%s\"", name); @@ -1447,6 +1448,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d cant_be_empty = (type & PW_TYPE_NOT_EMPTY); tmpl = (type & PW_TYPE_TMPL); multi = (type & PW_TYPE_MULTI); + ignore_dflt = (type & PW_TYPE_IGNORE_DEFAULT); if (attribute) required = true; if (required) cant_be_empty = true; /* May want to review this in the future... */ @@ -1470,7 +1472,7 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d * section, use the default value. */ if (!cp) { - if (deprecated) return 0; /* Don't set the default value */ + if (deprecated || ignore_dflt) return 0; /* Don't set the default value */ rcode = 1; value = dflt; @@ -1658,6 +1660,62 @@ int cf_item_parse(CONF_SECTION *cs, char const *name, unsigned int type, void *d cf_log_err(&(cs->item),"Failed expanding variable %s", name); return -1; } + + } else if (cf_new_escape && (cp->rhs_type == T_DOUBLE_QUOTED_STRING) && (strchr(value, '\\') != NULL)) { + char const *p = value; + char *s = buffer; + char *end = buffer + sizeof(buffer); + unsigned int x; + + /* + * We pass !cf_new_escape() to gettoken() when we parse the RHS of a CONF_PAIR + * above. But gettoken() unescapes the \", and doesn't unescape anything else. + * So we do it here. + */ + while (*p && (s < end)) { + if (*p != '\\') { + *(s++) = *(p++); + continue; + } + + p++; + + switch (*p) { + case 'r': + *s++ = '\r'; + break; + case 'n': + *s++ = '\n'; + break; + case 't': + *s++ = '\t'; + break; + + default: + if (*p >= '0' && *p <= '9' && + sscanf(p, "%3o", &x) == 1) { + if (!x) { + cf_log_err(&(cs->item), "Cannot have embedded zeros in value for %s", name); + return -1; + } + + *s++ = x; + p += 2; + } else + *s++ = *p; + break; + } + p++; + } + + if (s == end) { + cf_log_err(&(cs->item), "Failed expanding value for %s", name); + return -1; + } + + *s = '\0'; + + value = buffer; } if (cant_be_empty && (value[0] == '\0')) goto cant_be_empty; diff --git a/src/main/detail.c b/src/main/detail.c index a5e8437..3b7e382 100644 --- a/src/main/detail.c +++ b/src/main/detail.c @@ -910,8 +910,16 @@ open_file: rad_assert(vp != NULL); fr_pair_add(&packet->vps, vp); } + + /* + * Update Acct-Delay-Time, but make sure that it doesn't go backwards. + */ if (data->timestamp != 0) { - vp->vp_integer += time(NULL) - data->timestamp; + time_t now = time(NULL); + + if (((time_t) data->timestamp) < now) { + vp->vp_integer += time(NULL) - data->timestamp; + } } } diff --git a/src/main/exfile.c b/src/main/exfile.c index 59e6a05..1b498ce 100644 --- a/src/main/exfile.c +++ b/src/main/exfile.c @@ -170,7 +170,20 @@ static int exfile_open_mkdir(exfile_t *ef, char const *filename, mode_t permissi oflag = O_RDWR; } - fd = open(filename, oflag, permissions); + /* + * Just dup stdout / stderr if it's possible. + */ + if ((default_log.dst == L_DST_STDOUT) && + (strcmp(filename, "/dev/stdout") == 0)) { + fd = dup(STDOUT_FILENO); + + } else if ((default_log.dst == L_DST_STDERR) && + (strcmp(filename, "/dev/stderr") == 0)) { + fd = dup(STDERR_FILENO); + } else { + fd = open(filename, oflag, permissions); + } + if (fd < 0) { fr_strerror_printf("Failed to open file %s: %s", filename, strerror(errno)); diff --git a/src/main/listen.c b/src/main/listen.c index ee73a57..a6ff080 100644 --- a/src/main/listen.c +++ b/src/main/listen.c @@ -55,7 +55,7 @@ RCSID("$Id$") #ifdef WITH_TLS #include <netinet/tcp.h> -# ifdef __APPLE__ +# if defined(__APPLE__) || defined(__FreeBSD__) || defined(__illumos__) || defined(__sun__) # if !defined(SOL_TCP) && defined(IPPROTO_TCP) # define SOL_TCP IPPROTO_TCP # endif @@ -385,6 +385,7 @@ int rad_status_server(REQUEST *request) if (sock->state == LISTEN_TLS_CHECKING) { int autz_type = PW_AUTZ_TYPE; char const *name = "Autz-Type"; + rad_listen_t *listener = request->listener; if (request->listener->type == RAD_LISTEN_ACCT) { autz_type = PW_ACCT_TYPE; @@ -404,11 +405,22 @@ int rad_status_server(REQUEST *request) if ((rcode == RLM_MODULE_OK) || (rcode == RLM_MODULE_UPDATED)) { RDEBUG("(TLS) Connection is authorized"); request->reply->code = PW_CODE_ACCESS_ACCEPT; + + listener->status = RAD_LISTEN_STATUS_RESUME; + + rad_assert(sock->request->packet != request->packet); + + sock->state = LISTEN_TLS_SETUP; + } else { RWDEBUG("(TLS) Connection is not authorized - closing TCP socket."); request->reply->code = PW_CODE_ACCESS_REJECT; + + listener->status = RAD_LISTEN_STATUS_EOL; + listener->tls = NULL; /* parent owns this! */ } + radius_update_listener(listener); return 0; } } @@ -518,6 +530,123 @@ int rad_status_server(REQUEST *request) return 0; } +static void blastradius_checks(RADIUS_PACKET *packet, RADCLIENT *client) +{ + if (client->require_ma == FR_BOOL_TRUE) return; + + if (client->require_ma == FR_BOOL_AUTO) { + if (!packet->message_authenticator) { + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + ERROR("BlastRADIUS check: Received packet without Message-Authenticator."); + ERROR("Setting \"require_message_authenticator = false\" for client %s", client->shortname); + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); + ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + client->require_ma = FR_BOOL_FALSE; + + /* + * And fall through to the + * limit_proxy_state checks, which might + * complain again. Oh well, maybe that + * will make people read the messages. + */ + + } else if (packet->eap_message) { + /* + * Don't set it to "true" for packets + * with EAP-Message. It's already + * required there, and we might get a + * non-EAP packet with (or without) + * Message-Authenticator + */ + return; + } else { + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + ERROR("BlastRADIUS check: Received packet with Message-Authenticator."); + ERROR("Setting \"require_message_authenticator = true\" for client %s", client->shortname); + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + ERROR("It looks like the client has been updated to protect from the BlastRADIUS attack."); + ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname); + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + client->require_ma = FR_BOOL_TRUE; + return; + } + + } + + /* + * If all of the checks are turned off, then complain for every packet we receive. + */ + if (client->limit_proxy_state == FR_BOOL_FALSE) { + /* + * We have a Message-Authenticator, and it's valid. We don't need to compain. + */ + if (packet->message_authenticator) return; + + if (!fr_debug_lvl) return; /* easier than checking for each line below */ + + DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + DEBUG("BlastRADIUS check: Received packet without Message-Authenticator."); + DEBUG("YOU MUST SET \"require_message_authenticator = true\", or"); + DEBUG("YOU MUST SET \"limit_proxy_state = true\" for client %s", client->shortname); + DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + DEBUG("The packet does not contain Message-Authenticator, which is a security issue"); + DEBUG("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); + DEBUG("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); + DEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + return; + } + + /* + * Don't complain here. rad_packet_ok() will instead + * complain about every packet with Proxy-State but which + * is missing Message-Authenticator. + */ + if (client->limit_proxy_state == FR_BOOL_TRUE) { + return; + } + + if (packet->proxy_state && !packet->message_authenticator) { + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + ERROR("BlastRADIUS check: Received packet with Proxy-State, but without Message-Authenticator."); + ERROR("This is either a BlastRADIUS attack, OR"); + ERROR("the client is a proxy RADIUS server which has not been upgraded."); + ERROR("Setting \"limit_proxy_state = false\" for client %s", client->shortname); + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + ERROR("UPGRADE THE CLIENT AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); + ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + client->limit_proxy_state = FR_BOOL_FALSE; + + } else { + client->limit_proxy_state = FR_BOOL_TRUE; + + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + if (!packet->proxy_state) { + ERROR("BlastRADIUS check: Received packet without Proxy-State."); + } else { + ERROR("BlastRADIUS check: Received packet with Proxy-State and Message-Authenticator."); + } + + ERROR("Setting \"limit_proxy_state = true\" for client %s", client->shortname); + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + if (!packet->message_authenticator) { + ERROR("The packet does not contain Message-Authenticator, which is a security issue."); + ERROR("UPGRADE THE CLIENT AS YOUR NETWORK MAY BE VULNERABLE TO THE BLASTRADIUS ATTACK."); + ERROR("Once the client is upgraded, set \"require_message_authenticator = true\" for client %s", client->shortname); + } else { + ERROR("The packet contains Message-Authenticator."); + if (!packet->eap_message) ERROR("The client has likely been upgraded to protect from the attack."); + ERROR("Please set \"require_message_authenticator = true\" for client %s", client->shortname); + } + ERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } +} + #ifdef WITH_TCP static int dual_tcp_recv(rad_listen_t *listener) { @@ -594,6 +723,21 @@ static int dual_tcp_recv(rad_listen_t *listener) switch (packet->code) { case PW_CODE_ACCESS_REQUEST: if (listener->type != RAD_LISTEN_AUTH) goto bad_packet; + + /* + * Enforce BlastRADIUS checks on TCP, too. + */ + if (!rad_packet_ok(packet, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2), NULL)) { + FR_STATS_INC(auth, total_malformed_requests); + rad_free(&sock->packet); + return 0; + } + + /* + * Perform BlastRADIUS checks and warnings. + */ + if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client); + FR_STATS_INC(auth, total_requests); fun = rad_authenticate; break; @@ -720,7 +864,12 @@ static int tls_sni_callback(SSL *ssl, UNUSED int *al, void *arg) #endif #ifdef WITH_RADIUSV11 -static const unsigned char radiusv11_alpn_protos[] = { +static const unsigned char radiusv11_allow_protos[] = { + 10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1', /* prefer this */ + 10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '0', +}; + +static const unsigned char radiusv11_require_protos[] = { 10, 'r', 'a', 'd', 'i', 'u', 's', '/', '1', '.', '1', }; @@ -750,23 +899,28 @@ static int radiusv11_server_alpn_cb(SSL *ssl, memcpy(&hack, &out, sizeof(out)); /* const issues */ /* - * The RADIUSv11 configuration for this socket is a combination of what we require, and what we + * The RADIUS/1.1 configuration for this socket is a combination of what we require, and what we * require of the client. */ switch (this->radiusv11) { /* - * If we forbid RADIUSv11, then we never advertised it via ALPN, and this callback should + * If we forbid RADIUS/1.1, then we never advertised it via ALPN, and this callback should * never have been registered. */ case FR_RADIUSV11_FORBID: - *out = NULL; - *outlen = 0; - return SSL_TLSEXT_ERR_OK; + fr_assert(0); + server = radiusv11_allow_protos + 11; + server_len = 11; + break; case FR_RADIUSV11_ALLOW: + server = radiusv11_allow_protos; + server_len = sizeof(radiusv11_allow_protos); + break; + case FR_RADIUSV11_REQUIRE: - server = radiusv11_alpn_protos; - server_len = sizeof(radiusv11_alpn_protos); + server = radiusv11_require_protos; + server_len = sizeof(radiusv11_require_protos); break; } @@ -786,6 +940,7 @@ static int radiusv11_server_alpn_cb(SSL *ssl, */ fr_assert(*outlen == 10); sock->radiusv11 = (server[9] == '1'); + sock->alpn_checked = true; RDEBUG("(TLS) ALPN server negotiated application protocol \"%.*s\"", (int) *outlen, server); return SSL_TLSEXT_ERR_OK; @@ -798,6 +953,26 @@ static int radiusv11_server_alpn_cb(SSL *ssl, return SSL_TLSEXT_ERR_ALERT_FATAL; } +static int radiusv11_client_hello_cb(UNUSED SSL *s, int *alert, void *arg) +{ + rad_listen_t *this = arg; + listen_socket_t *sock = this->data; + + /* + * The server_alpn_cb ran, and checked that the configured ALPN matches the negotiated one. + */ + if (sock->alpn_checked) return SSL_CLIENT_HELLO_SUCCESS; + + /* + * The server_alpn_cb did NOT run (???) but we still have a client hello. We require ALPN and + * none was negotiated, so we return an error. + */ + *alert = SSL_AD_NO_APPLICATION_PROTOCOL; + + return SSL_CLIENT_HELLO_ERROR; +} + + int fr_radiusv11_client_init(fr_tls_server_conf_t *tls); int fr_radiusv11_client_get_alpn(rad_listen_t *listener); @@ -805,13 +980,17 @@ int fr_radiusv11_client_init(fr_tls_server_conf_t *tls) { switch (tls->radiusv11) { case FR_RADIUSV11_ALLOW: - case FR_RADIUSV11_REQUIRE: - if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_alpn_protos, sizeof(radiusv11_alpn_protos)) != 0) { - ERROR("Failed setting RADIUSv11 negotiation flags"); + if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_allow_protos, sizeof(radiusv11_allow_protos)) != 0) { + fail_protos: + ERROR("Failed setting RADIUS/1.1 negotiation flags"); return -1; } break; + case FR_RADIUSV11_REQUIRE: + if (SSL_CTX_set_alpn_protos(tls->ctx, radiusv11_require_protos, sizeof(radiusv11_require_protos)) != 0) goto fail_protos; + break; + default: break; } @@ -827,41 +1006,53 @@ int fr_radiusv11_client_get_alpn(rad_listen_t *listener) SSL_get0_alpn_selected(sock->ssn->ssl, &data, &len); if (!data) { - DEBUG("(TLS) ALPN home server did not send any application protocol"); + DEBUG("(TLS) ALPN server did not send any application protocol"); if (listener->radiusv11 == FR_RADIUSV11_REQUIRE) { DEBUG("(TLS) We have 'radiusv11 = require', but the home server has not negotiated it - closing socket"); return -1; } - DEBUG("(TLS) ALPN assuming historical RADIUS"); - return 0; + DEBUG("(TLS) ALPN assuming \"radius/1.0\""); + return 0; /* allow radius/1.0 */ } - DEBUG("(TLS) ALPN home server sent application protocol \"%.*s\"", (int) len, data); + DEBUG("(TLS) ALPN server sent application protocol \"%.*s\"", (int) len, data); if (len != 10) { radiusv11_unknown: - DEBUG("(TLS) ALPN home server sent unknown application protocol - closing connection"); + DEBUG("(TLS) ALPN server sent unknown application protocol - closing connection to home server"); return -1; } /* - * Should always be "radius/1.1". The server MUST echo back one of the strings + * Should always be "radius/1.0" or "radius/1.1". The server MUST echo back one of the strings * we sent. If it doesn't, it's a bad server. */ - if (memcmp(data, "radius/1.1", 10) != 0) goto radiusv11_unknown; + if (memcmp(data, "radius/1.", 9) != 0) goto radiusv11_unknown; + + if ((data[9] != '0') && (data[9] != '1')) goto radiusv11_unknown; /* * Double-check what the server sent us. It SHOULD be sane, but it never hurts to check. */ switch (listener->radiusv11) { case FR_RADIUSV11_FORBID: - DEBUG("(TLS) ALPN home server sent \"radius/v1.1\" but we forbid it - closing connection to home server"); - return -1; + if (data[9] != '0') { + DEBUG("(TLS) ALPN server did not send \"radius/v1.0\" - closing connection to home server"); + return -1; + } + break; case FR_RADIUSV11_ALLOW: + sock->radiusv11 = (data[9] == '1'); + break; + case FR_RADIUSV11_REQUIRE: - DEBUG("(TLS) ALPN using \"radius/1.1\""); + if (data[9] != '1') { + DEBUG("(TLS) ALPN server did not send \"radius/v1.1\" - closing connection to home server"); + return -1; + } + sock->radiusv11 = true; break; } @@ -1066,15 +1257,30 @@ static int dual_tcp_accept(rad_listen_t *listener) SSL_CTX_set_tlsext_servername_callback(this->tls->ctx, tls_sni_callback); SSL_CTX_set_tlsext_servername_arg(this->tls->ctx, this->tls); #ifdef WITH_RADIUSV11 - /* - * Default is "forbid" (0). In which case we don't set any ALPN callbacks, and - * the ServerHello does not contain an ALPN section. - */ - if (client->radiusv11 != FR_RADIUSV11_FORBID) { + switch (client->radiusv11) { + /* + * We don't set any callbacks. If the client sends ALPN (or not), we + * just do normal RADIUS. + */ + case FR_RADIUSV11_FORBID: + DEBUG("(TLS) ALPN radiusv11 = forbid"); + break; + + /* + * Setting the client hello callback catches the case where we send ALPN, + * and the client doesn't send anything. + */ + case FR_RADIUSV11_REQUIRE: + SSL_CTX_set_client_hello_cb(this->tls->ctx, radiusv11_client_hello_cb, this); + /* FALL-THROUGH */ + + /* + * We're willing to do normal RADIUS, but we send ALPN, and then check if + * (or what) the client sends back as ALPN. + */ + case FR_RADIUSV11_ALLOW: SSL_CTX_set_alpn_select_cb(this->tls->ctx, radiusv11_server_alpn_cb, this); DEBUG("(TLS) ALPN radiusv11 = allow / require"); - } else { - DEBUG("(TLS) ALPN radiusv11 = forbid"); } #endif } @@ -1313,6 +1519,12 @@ static CONF_PARSER limit_config[] = { { "max_connections", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.max_connections), "16" }, { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.lifetime), "0" }, { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.idle_timeout), STRINGIFY(30) }, +#ifdef SO_RCVTIMEO + { "read_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.read_timeout), NULL }, +#endif +#ifdef SO_SNDTIMEO + { "write_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, listen_socket_t, limit.write_timeout), NULL }, +#endif #endif CONF_PARSER_TERMINATOR }; @@ -1467,6 +1679,8 @@ int common_socket_parse(CONF_SECTION *cs, rad_listen_t *this) return -1; } + this->tls->name = "RADIUS/TLS"; + #ifdef HAVE_PTHREAD_H if (pthread_mutex_init(&sock->mutex, NULL) < 0) { rad_assert(0 == 1); @@ -1822,8 +2036,6 @@ static int stats_socket_recv(rad_listen_t *listener) rcode = rad_recv_header(listener->fd, &src_ipaddr, &src_port, &code); if (rcode < 0) return 0; - FR_STATS_INC(auth, total_requests); - if (rcode < 20) { /* RADIUS_HDR_LEN */ if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); FR_STATS_INC(auth, total_malformed_requests); @@ -1871,7 +2083,6 @@ static int stats_socket_recv(rad_listen_t *listener) } #endif - /* * Check if an incoming request is "ok" * @@ -1947,7 +2158,7 @@ static int auth_socket_recv(rad_listen_t *listener) * Now that we've sanity checked everything, receive the * packet. */ - packet = rad_recv(ctx, listener->fd, client->message_authenticator); + packet = rad_recv(ctx, listener->fd, (client->require_ma == FR_BOOL_TRUE) | ((client->limit_proxy_state == FR_BOOL_TRUE) << 2)); if (!packet) { FR_STATS_INC(auth, total_malformed_requests); if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); @@ -1955,6 +2166,11 @@ static int auth_socket_recv(rad_listen_t *listener) return 0; } + /* + * Perform BlastRADIUS checks and warnings. + */ + if (packet->code == PW_CODE_ACCESS_REQUEST) blastradius_checks(packet, client); + #ifdef __APPLE__ #ifdef WITH_UDPFROMTO /* @@ -2343,7 +2559,7 @@ static int coa_socket_recv(rad_listen_t *listener) * Now that we've sanity checked everything, receive the * packet. */ - packet = rad_recv(ctx, listener->fd, client->message_authenticator); + packet = rad_recv(ctx, listener->fd, client->require_ma); if (!packet) { FR_STATS_INC(coa, total_malformed_requests); if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); @@ -2663,7 +2879,7 @@ static int proxy_socket_encode(RADIUSV11_UNUSED rad_listen_t *listener, REQUEST } -static int proxy_socket_decode(UNUSED rad_listen_t *listener, REQUEST *request) +static int proxy_socket_decode(RADIUSV11_UNUSED rad_listen_t *listener, REQUEST *request) { #ifdef WITH_RADIUSV11 listen_socket_t *sock = listener->data; @@ -2907,6 +3123,9 @@ static int listen_bind(rad_listen_t *this) */ if (sock->interface) { #ifdef SO_BINDTODEVICE + /* + * Linux: Bind to an interface by name. + */ struct ifreq ifreq; memset(&ifreq, 0, sizeof(ifreq)); @@ -2919,45 +3138,81 @@ static int listen_bind(rad_listen_t *this) if (rcode < 0) { close(this->fd); ERROR("Failed binding to interface %s: %s", - sock->interface, fr_syserror(errno)); + sock->interface, fr_syserror(errno)); return -1; - } /* else it worked. */ + } #else + + /* + * If we don't bind to an interface by name, we usually bind to it by index. + */ + int idx = if_nametoindex(sock->interface); + + if (idx == 0) { + close(this->fd); + ERROR("Failed finding interface %s: %s", + sock->interface, fr_syserror(errno)); + return -1; + } + +#ifdef IP_BOUND_IF + /* + * OSX / ?BSD / Solaris: bind to interface by index for IPv4 + */ + if (sock->my_ipaddr.af == AF_INET) { + rad_suid_up(); + rcode = setsockopt(this->fd, IPPROTO_IP, IP_BOUND_IF, &idx, sizeof(idx)); + rad_suid_down(); + if (rcode < 0) { + close(this->fd); + ERROR("Failed binding to interface %s: %s", + sock->interface, fr_syserror(errno)); + return -1; + } + } else +#endif + +#ifdef IPV6_BOUND_IF + /* + * OSX / ?BSD / Solaris: bind to interface by index for IPv6 + */ + if (sock->my_ipaddr.af == AF_INET6) { + rad_suid_up(); + rcode = setsockopt(this->fd, IPPROTO_IPV6, IPV6_BOUND_IF, &idx, sizeof(idx)); + rad_suid_down(); + if (rcode < 0) { + close(this->fd); + ERROR("Failed binding to interface %s: %s", + sock->interface, fr_syserror(errno)); + return -1; + } + } else +#endif + #ifdef HAVE_STRUCT_SOCKADDR_IN6 #ifdef HAVE_NET_IF_H /* - * Odds are that any system supporting "bind to - * device" also supports IPv6, so this next bit - * isn't necessary. But it's here for - * completeness. - * - * If we're doing IPv6, and the scope hasn't yet - * been defined, set the scope to the scope of - * the interface. + * Otherwise generic IPv6: set the scope to the + * interface, and hope that all of the read/write + * routines respect that. */ if (sock->my_ipaddr.af == AF_INET6) { if (sock->my_ipaddr.scope == 0) { - sock->my_ipaddr.scope = if_nametoindex(sock->interface); - if (sock->my_ipaddr.scope == 0) { - close(this->fd); - ERROR("Failed finding interface %s: %s", - sock->interface, fr_syserror(errno)); - return -1; - } - } /* else scope was defined: we're OK. */ + sock->my_ipaddr.scope = idx; + } /* else scope was already defined */ } else #endif #endif - /* - * IPv4: no link local addresses, - * and no bind to device. - */ + + /* + * IPv4, or no socket options to bind to interface. + */ { close(this->fd); ERROR("Failed binding to interface %s: \"bind to device\" is unsupported", sock->interface); return -1; } -#endif +#endif /* SO_BINDTODEVICE */ } #ifdef WITH_TCP @@ -3067,6 +3322,7 @@ static int listen_bind(rad_listen_t *this) int on = 1; if (setsockopt(this->fd, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) { + close(this->fd); ERROR("Can't set broadcast option: %s", fr_syserror(errno)); return -1; @@ -3115,6 +3371,7 @@ static int listen_bind(rad_listen_t *this) memset(&src, 0, sizeof_src); if (getsockname(this->fd, (struct sockaddr *) &src, &sizeof_src) < 0) { + close(this->fd); ERROR("Failed getting socket name: %s", fr_syserror(errno)); return -1; @@ -3122,6 +3379,7 @@ static int listen_bind(rad_listen_t *this) if (!fr_sockaddr2ipaddr(&src, sizeof_src, &sock->my_ipaddr, &sock->my_port)) { + close(this->fd); ERROR("Socket has unsupported address family"); return -1; } @@ -3131,9 +3389,9 @@ static int listen_bind(rad_listen_t *this) #ifdef WITH_TCP if (sock->proto == IPPROTO_TCP) { /* - * Woker threads are blocking. + * If we dedicate a worker thread to each socket, then the socket is blocking. * - * Otherwise, they're non-blocking. + * Otherwise, all input TCP sockets are non-blocking. */ if (!this->workers) { if (fr_nonblock(this->fd) < 0) { @@ -3331,11 +3589,15 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t * FIXME: connect() is blocking! * We do this with the proxy mutex locked, which may * cause large delays! - * - * http://www.developerweb.net/forum/showthread.php?p=13486 */ this->fd = fr_socket_client_tcp(&home->src_ipaddr, - &home->ipaddr, home->port, false); + &home->ipaddr, home->port, +#ifdef WITH_TLS + !this->nonblock +#else + false +#endif + ); /* * Set max_requests, lifetime, and idle_timeout from the home server. @@ -3378,10 +3640,25 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t this->nonblock |= home->nonblock; +#ifdef TCP_NODELAY + /* + * Also set TCP_NODELAY, to force the data to be written quickly. + */ + if (sock->proto == IPPROTO_TCP) { + int on = 1; + + if (setsockopt(this->fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) { + ERROR("(TLS) Failed to set TCP_NODELAY: %s", fr_syserror(errno)); + goto error; + } + } +#endif + /* * Set non-blocking if it's configured. */ if (this->nonblock) { + fr_assert(0); if (fr_nonblock(this->fd) < 0) { ERROR("(TLS) Failed setting nonblocking for proxy socket '%s' - %s", buffer, fr_strerror()); goto error; @@ -3394,15 +3671,34 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t goto error; } -#ifdef TCP_NODELAY + } else { /* - * Also set TCP_NODELAY, to force the data to be written quickly. + * Only set timeouts when the socket is blocking. This allows blocking + * sockets to still time out when the underlying socket is dead. */ - if (sock->proto == IPPROTO_TCP) { - int on = 1; +#ifdef SO_RCVTIMEO + if (sock->limit.read_timeout) { + struct timeval tv; + + tv.tv_sec = sock->limit.read_timeout; + tv.tv_usec = 0; - if (setsockopt(this->fd, SOL_TCP, TCP_NODELAY, &on, sizeof(on)) < 0) { - ERROR("(TLS) Failed to set TCP_NODELAY: %s", fr_syserror(errno)); + if (setsockopt(this->fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)) < 0) { + ERROR("(TLS) Failed to set read_timeout: %s", fr_syserror(errno)); + goto error; + } + } +#endif + +#ifdef SO_SNDTIMEO + if (sock->limit.write_timeout) { + struct timeval tv; + + tv.tv_sec = sock->limit.write_timeout; + tv.tv_usec = 0; + + if (setsockopt(this->fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)) < 0) { + ERROR("(TLS) Failed to set write_timeout: %s", fr_syserror(errno)); goto error; } } @@ -3427,6 +3723,7 @@ rad_listen_t *proxy_new_listener(TALLOC_CTX *ctx, home_server_t *home, uint16_t } #endif + sock->connect_timeout = home->connect_timeout; this->recv = proxy_tls_recv; @@ -3575,7 +3872,9 @@ static rad_listen_t *listen_parse(CONF_SECTION *cs, char const *server) char const *value; fr_dlhandle handle; CONF_SECTION *server_cs; +#ifdef WITH_TCP char const *p; +#endif char buffer[32]; cp = cf_pair_find(cs, "type"); @@ -3864,7 +4163,9 @@ int listen_init(CONF_SECTION *config, rad_listen_t **head, bool spawn_flag) if (override) { cs = cf_section_sub_find_name2(config, "server", main_config.name); - if (cs) this->server = main_config.name; + if (!cs) cs = cf_section_sub_find_name2(config, "server", + "default"); + if (cs) this->server = cf_section_name2(cs); } *last = this; diff --git a/src/main/mainconfig.c b/src/main/mainconfig.c index 227ae4a..2b2dda8 100644 --- a/src/main/mainconfig.c +++ b/src/main/mainconfig.c @@ -73,6 +73,8 @@ static char const *gid_name = NULL; static char const *chroot_dir = NULL; static bool allow_core_dumps = false; static char const *radlog_dest = NULL; +static char const *require_message_authenticator = NULL; +static char const *limit_proxy_state = NULL; /* * These are not used anywhere else.. @@ -87,6 +89,56 @@ static bool do_colourise = false; static char const *radius_dir = NULL; //!< Path to raddb directory +#ifndef HAVE_KQUEUE +static uint32_t max_fds = 0; +#endif + +static const FR_NAME_NUMBER fr_bool_auto_names[] = { + { "false", FR_BOOL_FALSE }, + { "no", FR_BOOL_FALSE }, + { "0", FR_BOOL_FALSE }, + + { "true", FR_BOOL_TRUE }, + { "yes", FR_BOOL_TRUE }, + { "1", FR_BOOL_TRUE }, + + { "auto", FR_BOOL_AUTO }, + + { NULL, 0 } +}; + +/* + * Get decent values for false / true / auto + */ +int fr_bool_auto_parse(CONF_PAIR *cp, fr_bool_auto_t *out, char const *str) +{ + int value; + + /* + * Don't change anything. + */ + if (!str) return 0; + + value = fr_str2int(fr_bool_auto_names, str, -1); + if (value >= 0) { + *out = value; + return 0; + } + + /* + * This should never happen, as the defaults are in the + * source code. If there's no CONF_PAIR, and there's a + * parse error, then the source code is wrong. + */ + if (!cp) { + fprintf(stderr, "%s: Error - Invalid value in configuration", main_config.name); + return -1; + } + + cf_log_err(cf_pair_to_item(cp), "Invalid value for \"%s\"", cf_pair_attr(cp)); + return -1; +} + /********************************************************************** * * We need to figure out where the logs go, before doing anything @@ -160,6 +212,8 @@ static const CONF_PARSER security_config[] = { { "max_attributes", FR_CONF_POINTER(PW_TYPE_INTEGER, &fr_max_attributes), STRINGIFY(0) }, { "reject_delay", FR_CONF_POINTER(PW_TYPE_TIMEVAL, &main_config.reject_delay), STRINGIFY(0) }, { "status_server", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.status_server), "no"}, + { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING, &require_message_authenticator), "auto"}, + { "limit_proxy_state", FR_CONF_POINTER(PW_TYPE_STRING, &limit_proxy_state), "auto"}, #ifdef ENABLE_OPENSSL_VERSION_CHECK { "allow_vulnerable_openssl", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.allow_vulnerable_openssl), "no"}, #endif @@ -195,8 +249,12 @@ static const CONF_PARSER server_config[] = { { "panic_action", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.panic_action), NULL}, { "hostname_lookups", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &fr_dns_lookups), "no" }, { "max_request_time", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_request_time), STRINGIFY(MAX_REQUEST_TIME) }, + { "proxy_dedup_window", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.proxy_dedup_window), "1" }, { "cleanup_delay", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.cleanup_delay), STRINGIFY(CLEANUP_DELAY) }, { "max_requests", FR_CONF_POINTER(PW_TYPE_INTEGER, &main_config.max_requests), STRINGIFY(MAX_REQUESTS) }, +#ifndef HAVE_KQUEUE + { "max_fds", FR_CONF_POINTER(PW_TYPE_INTEGER, &max_fds), "512" }, +#endif { "postauth_client_lost", FR_CONF_POINTER(PW_TYPE_BOOLEAN, &main_config.postauth_client_lost), "no" }, { "pidfile", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.pid_file), "${run_dir}/radiusd.pid"}, { "checkrad", FR_CONF_POINTER(PW_TYPE_STRING, &main_config.checkrad), "${sbindir}/checkrad" }, @@ -864,6 +922,8 @@ int main_config_init(void) if (!main_config.dictionary_dir) { main_config.dictionary_dir = DICTDIR; } + main_config.require_ma = FR_BOOL_AUTO; + main_config.limit_proxy_state = FR_BOOL_AUTO; /* * About sizeof(REQUEST) + sizeof(RADIUS_PACKET) * 2 + sizeof(VALUE_PAIR) * 400 @@ -1144,6 +1204,10 @@ do {\ if ((main_config.reject_delay.tv_sec != 0) || (main_config.reject_delay.tv_usec != 0)) { FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, >=, 1, 0); } + + FR_INTEGER_BOUND_CHECK("proxy_dedup_window", main_config.proxy_dedup_window, <=, 10); + FR_INTEGER_BOUND_CHECK("proxy_dedup_window", main_config.proxy_dedup_window, >=, 1); + FR_TIMEVAL_BOUND_CHECK("reject_delay", &main_config.reject_delay, <=, 10, 0); FR_INTEGER_BOUND_CHECK("cleanup_delay", main_config.cleanup_delay, <=, 30); @@ -1159,6 +1223,46 @@ do {\ main_config.init_delay.tv_sec = 0; main_config.init_delay.tv_usec = 2* (1000000 / 3); + { + CONF_PAIR *cp = NULL; + + subcs = cf_section_sub_find(cs, "security"); + if (subcs) cp = cf_pair_find(subcs, "require_message_authenticator"); + if (fr_bool_auto_parse(cp, &main_config.require_ma, require_message_authenticator) < 0) { + cf_file_free(cs); + return -1; + } + + if (subcs) cp = cf_pair_find(subcs, "limit_proxy_state"); + if (fr_bool_auto_parse(cp, &main_config.limit_proxy_state, limit_proxy_state) < 0) { + cf_file_free(cs); + return -1; + } + } + +#ifndef HAVE_KQUEUE + /* + * select() is limited to 1024 file descriptors. :( + */ + if (max_fds) { + if (max_fds > FD_SETSIZE) { + fr_ev_max_fds = FD_SETSIZE; + } else { + /* + * Round up to the next highest power of 2. + */ + max_fds--; + max_fds |= max_fds >> 1; + max_fds |= max_fds >> 2; + max_fds |= max_fds >> 4; + max_fds |= max_fds >> 8; + max_fds |= max_fds >> 16; + max_fds++; + fr_ev_max_fds = max_fds; + } + } +#endif + /* * Free the old configuration items, and replace them * with the new ones. diff --git a/src/main/map.c b/src/main/map.c index e59fcec..34683a2 100644 --- a/src/main/map.c +++ b/src/main/map.c @@ -1108,7 +1108,7 @@ int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t */ if (((map->lhs->tmpl_list == PAIR_LIST_COA) || (map->lhs->tmpl_list == PAIR_LIST_DM)) && !request->coa) { - if (request->parent) { + if (context->parent) { REDEBUG("You can only do 'update coa' when processing a packet which was received from the network"); return -2; } diff --git a/src/main/modcall.c b/src/main/modcall.c index aa6abf8..5a3116c 100644 --- a/src/main/modcall.c +++ b/src/main/modcall.c @@ -311,7 +311,7 @@ static rlm_rcode_t CC_HINT(nonnull) call_modsingle(rlm_components_t component, m */ blocked = (request->master_state == REQUEST_STOP_PROCESSING); if (blocked) { - RWARN("Module %s became unblocked", sp->modinst->entry->name); + RWARN("Module %s(%s) became unblocked", sp->modinst->name, sp->modinst->entry->name); } fail: diff --git a/src/main/modules.c b/src/main/modules.c index fd4334d..9ccb310 100644 --- a/src/main/modules.c +++ b/src/main/modules.c @@ -571,10 +571,10 @@ static int module_conf_parse(module_instance_t *node, void **handle) * If there is supposed to be instance data, allocate it now. * Also parse the configuration data, if required. */ - if (node->entry->module->inst_size) { - *handle = talloc_zero_array(node, uint8_t, node->entry->module->inst_size); - rad_assert(*handle); + *handle = talloc_zero_array(node, uint8_t, node->entry->module->inst_size); + rad_assert(*handle); + if (node->entry->module->inst_size) { talloc_set_name(*handle, "rlm_%s_t", node->entry->module->name ? node->entry->module->name : "config"); diff --git a/src/main/process.c b/src/main/process.c index ed77839..9880e34 100644 --- a/src/main/process.c +++ b/src/main/process.c @@ -1006,6 +1006,12 @@ static void request_cleanup_delay_init(REQUEST *request) #ifdef HAVE_PTHREAD_H rad_assert(request->child_pid == NO_SUCH_CHILD_PID); #endif + + /* + * Set the statistics immediately if we can. + */ + request_stats_final(request); + STATE_MACHINE_TIMER(FR_ACTION_TIMER); return; } @@ -1258,6 +1264,9 @@ static void request_cleanup_delay(REQUEST *request, int action) #ifdef DEBUG_STATE_MACHINE if (rad_debug_lvl) printf("(%u) ********\tNEXT-STATE %s -> %s\n", request->number, __FUNCTION__, "request_cleanup_delay"); #endif + + request_stats_final(request); + STATE_MACHINE_TIMER(FR_ACTION_TIMER); return; } /* else it's time to clean up */ @@ -1486,7 +1495,7 @@ static void request_finish(REQUEST *request, int action) /* * Maybe originate a CoA request. */ - if ((action == FR_ACTION_RUN) && !request->proxy && request->coa) { + if ((action == FR_ACTION_RUN) && (!request->proxy || request->proxy->dst_port == 0) && request->coa) { request_coa_originate(request); } #endif @@ -1591,9 +1600,14 @@ static void request_finish(REQUEST *request, int action) #ifdef WITH_PROXY /* * If we timed out a proxy packet, don't delay - * the reject any more. + * the reject any more. Or, if we proxied it to + * a real home server, then don't delay it. + * + * We don't want to have each proxy in a chain + * adding their own reject delay, which would + * result in N*reject_delays being applied. */ - if (request->proxy && !request->proxy_reply) { + if (request->proxy && (!request->proxy_reply || request->proxy->dst_port != 0)) { request->response_delay.tv_sec = 0; request->response_delay.tv_usec = 0; } @@ -2024,6 +2038,10 @@ static REQUEST *request_setup(TALLOC_CTX *ctx, rad_listen_t *listener, RADIUS_PA return NULL; } +#ifdef WITH_RADIUSV11 + request->reply->radiusv11 = packet->radiusv11; +#endif + request->listener = listener; request->client = client; request->packet = talloc_steal(request, packet); @@ -2286,16 +2304,6 @@ static void remove_from_proxy_hash_nl(REQUEST *request, bool yank) if (!request->in_proxy_hash) return; -#ifdef COA_TUNNEL - /* - * Track how many IDs are used. This information - * helps the listen_coa_find() function get a - * listener which has free IDs. - */ - rad_assert(request->proxy_listener->num_ids_used > 0); - request->proxy_listener->num_ids_used--; -#endif - fr_packet_list_id_free(proxy_list, request->proxy, yank); request->in_proxy_hash = false; @@ -2341,6 +2349,18 @@ static void remove_from_proxy_hash_nl(REQUEST *request, bool yank) if (request->proxy_listener) { request->proxy_listener->count--; + +#ifdef WITH_COA_TUNNEL + /* + * Track how many IDs are used. This information + * helps the listen_coa_find() function get a + * listener which has free IDs. + */ + if (request->proxy_listener->send_coa) { + rad_assert(request->proxy_listener->num_ids_used > 0); + request->proxy_listener->num_ids_used--; + } +#endif } request->proxy_listener = NULL; @@ -2361,18 +2381,6 @@ static void remove_from_proxy_hash(REQUEST *request) */ if (!request->in_proxy_hash) return; -#ifdef WITH_TCP - /* - * Status-Server packets aren't removed from the proxy hash. They're reused. - * - * Unless we're tearing down the listener. - */ - if ((request->proxy->proto == IPPROTO_TCP) && (request->proxy->code == PW_CODE_STATUS_SERVER) && - request->proxy_listener && (request->proxy_listener->status < RAD_LISTEN_STATUS_EOL)) { - return; - } -#endif - /* * The "not in hash" flag is definitive. However, if the * flag says that it IS in the hash, there might still be @@ -2493,13 +2501,13 @@ static int insert_into_proxy_hash(REQUEST *request) goto fail; } -#ifdef COA_TUNNEL +#ifdef WITH_COA_TUNNEL /* * Track how many IDs are used. This information * helps the listen_coa_find() function get a * listener which has free IDs. */ - request->proxy_listener->num_ids_used++; + if (request->proxy_listener->send_coa) request->proxy_listener->num_ids_used++; #endif /* @@ -2790,16 +2798,78 @@ int request_proxy_reply(RADIUS_PACKET *packet) * server core, but I guess we can fix that later. */ if (!request->proxy_reply) { + decode_fail_t reason; + + /* + * If the home server configuration requires a Message-Authenticator, then set the flag, + * but only if the proxied packet is Access-Request or Status-Sercer. + * + * The realms.c file already clears require_ma for TLS connections. + */ + bool require_ma = (request->home_server->require_ma == FR_BOOL_TRUE) && (request->proxy->code == PW_CODE_ACCESS_REQUEST); + if (!request->home_server) { proxy_reply_too_late(request); return 0; } + if (!rad_packet_ok(packet, require_ma, &reason)) { + DEBUG("Ignoring invalid packet - %s", fr_strerror()); + return 0; + } + if (rad_verify(packet, request->proxy, request->home_server->secret) != 0) { DEBUG("Ignoring spoofed proxy reply. Signature is invalid"); return 0; } + + /* + * BlastRADIUS checks. We're running in the main + * listener thread, so there's no conflict + * checking or setting these fields. + */ + if ((request->proxy->code == PW_CODE_ACCESS_REQUEST) && +#ifdef WITH_TLS + !request->home_server->tls && +#endif + !packet->eap_message) { + if (request->home_server->require_ma == FR_BOOL_AUTO) { + if (!packet->message_authenticator) { + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + RERROR("BlastRADIUS check: Received response to Access-Request without Message-Authenticator."); + RERROR("Setting \"require_message_authenticator = false\" for home_server %s", request->home_server->name); + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + RERROR("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); + RERROR("Once the home_server is upgraded, set \"require_message_authenticator = true\" for home_server %s.", request->home_server->name); + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + request->home_server->require_ma = FR_BOOL_FALSE; + } else { + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + RERROR("BlastRADIUS check: Received response to Access-Request with Message-Authenticator."); + RERROR("Setting \"require_message_authenticator = true\" for home_server %s", request->home_server->name); + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + RERROR("It looks like the home server has been updated to protect from the BlastRADIUS attack."); + RERROR("Please set \"require_message_authenticator = true\" for home_server %s", request->home_server->name); + RERROR("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + + request->home_server->require_ma = FR_BOOL_TRUE; + } + + } else if (fr_debug_lvl && (request->home_server->require_ma == FR_BOOL_FALSE) && !packet->message_authenticator) { + /* + * If it's "no" AND we don't have a Message-Authenticator, then complain on every packet. + */ + RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + RDEBUG("BlastRADIUS check: Received packet without Message-Authenticator from home_server %s", request->home_server->name); + RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + RDEBUG("The packet does not contain Message-Authenticator, which is a security issue"); + RDEBUG("UPGRADE THE HOME SERVER AS YOUR NETWORK IS VULNERABLE TO THE BLASTRADIUS ATTACK."); + RDEBUG("Once the home server is upgraded, set \"require_message_authenticator = true\" for home_server %s", request->home_server->name); + RDEBUG("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + } + } } /* @@ -2863,6 +2933,18 @@ int request_proxy_reply(RADIUS_PACKET *packet) #ifdef WITH_STATS /* + * The average includes our time to receive packets and + * look them up in the hashes, which should be the same + * for all packets. + * + * We update the response time only for the FIRST packet + * we receive. + */ + if (request->home_server->ema.window > 0) { + radius_stats_ema(&request->home_server->ema, &request->proxy->timestamp, &now); + } + + /* * Update the proxy listener stats here, because only one * thread accesses that at a time. The home_server and * main proxy_*_stats structures are updated once the @@ -3193,6 +3275,14 @@ static int request_will_proxy(REQUEST *request) pool = home_pool_byname(vp->vp_strvalue, pool_type); /* + * If we didn't find an auth only or acct only pool + * fall-back to those which do both. + */ + if (!pool && ((pool_type == HOME_TYPE_AUTH) || (pool_type == HOME_TYPE_ACCT))) { + pool = home_pool_byname(vp->vp_strvalue, HOME_TYPE_AUTH_ACCT); + } + + /* * Send it directly to a home server (i.e. NAS) */ } else if (((vp = fr_pair_find_by_num(request->config, PW_PACKET_DST_IP_ADDRESS, 0, TAG_ANY)) != NULL) || @@ -3292,6 +3382,15 @@ static int request_will_proxy(REQUEST *request) * Find the home server by name. */ home = home_server_byname(vp->vp_strvalue, type); + + /* + * If we didn't find an auth only or acct only home server + * fall-back to those which do both. + */ + if (!home && ((type == HOME_TYPE_AUTH) || (type == HOME_TYPE_ACCT))) { + home = home_server_byname(vp->vp_strvalue, HOME_TYPE_AUTH_ACCT); + } + if (!home) { RWDEBUG("No such home server %s", vp->vp_strvalue); return 0; @@ -3361,7 +3460,7 @@ static int request_will_proxy(REQUEST *request) home = home_server_ldb(realmname, pool, request); if (!home) { - REDEBUG2("Failed to find live home server: Cancelling proxy"); + REDEBUG2("Failed to find live home server for realm %s: Cancelling proxy", realmname); return -1; } @@ -3373,7 +3472,11 @@ do_home: * Once we've decided to proxy a request, we cannot send * a CoA packet. So we free up any CoA packet here. */ - if (request->coa) request_done(request->coa, FR_ACTION_COA_CANCELLED); + if (request->coa) { + RWDEBUG("Cannot proxy and originate CoA packets at the same time. Cancelling CoA request"); + request_done(request->coa, FR_ACTION_COA_CANCELLED); + request->coa = NULL; + } #endif /* @@ -3605,6 +3708,7 @@ static int request_proxy(REQUEST *request) if (request->coa) { RWDEBUG("Cannot proxy and originate CoA packets at the same time. Cancelling CoA request"); request_done(request->coa, FR_ACTION_COA_CANCELLED); + request->coa = NULL; } #endif @@ -3817,6 +3921,7 @@ static void request_ping(REQUEST *request, int action) break; case FR_ACTION_PROXY_REPLY: + default: rad_assert(request->in_proxy_hash); request->home_server->num_received_pings++; @@ -3855,9 +3960,10 @@ static void request_ping(REQUEST *request, int action) mark_home_server_alive(request, home); break; - default: + case FR_ACTION_RUN: + case FR_ACTION_DUP: RDEBUG3("%s: Ignoring action %s", __FUNCTION__, action_codes[action]); - break; + return; } rad_assert(!request->in_request_hash); @@ -4339,10 +4445,10 @@ static void proxy_wait_for_reply(REQUEST *request, int action) * and should be suppressed by the proxy. */ when = request->proxy->timestamp; - when.tv_sec++; + when.tv_sec += main_config.proxy_dedup_window; if (timercmp(&now, &when, <)) { - DEBUG2("Suppressing duplicate proxied request (too fast) to home server %s port %d proto TCP - ID: %d", + DEBUG2("Suppressing duplicate proxied request (too fast) to home server %s port %d - ID: %d", inet_ntop(request->proxy->dst_ipaddr.af, &request->proxy->dst_ipaddr.ipaddr, buffer, sizeof(buffer)), @@ -4543,9 +4649,9 @@ static void request_coa_originate(REQUEST *request) VERIFY_REQUEST(request); rad_assert(request->coa != NULL); - rad_assert(request->proxy == NULL); + rad_assert(request->proxy == NULL || request->proxy->dst_port == 0); rad_assert(!request->in_proxy_hash); - rad_assert(request->proxy_reply == NULL); + rad_assert(request->proxy_reply == NULL || request->proxy_reply->src_port == 0); /* * Check whether we want to originate one, or cancel one. @@ -5018,7 +5124,11 @@ static bool coa_max_time(REQUEST *request) buffer, sizeof(buffer)), request->proxy->dst_port, mrd); - request_done(request, FR_ACTION_DONE); + if (setup_post_proxy_fail(request)) { + request_queue_or_run(request, coa_no_reply); + } else { + request_done(request, FR_ACTION_DONE); + } return true; } @@ -5386,7 +5496,6 @@ static void listener_free_cb(void *ctx) talloc_free(this); } -#ifdef WITH_TCP #ifdef WITH_PROXY static int proxy_eol_cb(void *ctx, void *data) { @@ -5426,7 +5535,6 @@ static int proxy_eol_cb(void *ctx, void *data) return 0; } #endif /* WITH_PROXY */ -#endif /* WITH_TCP */ static void event_new_fd(rad_listen_t *this) { @@ -5602,6 +5710,7 @@ static void event_new_fd(rad_listen_t *this) */ this->print(this, buffer, sizeof(buffer)); ERROR("Failed adding event handler for socket %s: %s", buffer, fr_strerror()); + this->status = RAD_LISTEN_STATUS_EOL; goto listener_is_eol; } /* end of INIT */ @@ -5645,6 +5754,7 @@ static void event_new_fd(rad_listen_t *this) fr_event_fd_delete(el, 0, this->fd); this->status = RAD_LISTEN_STATUS_REMOVE_NOW; } +#endif /* WITH_TCP */ /* * The socket has had a catastrophic error. Close it. @@ -5708,7 +5818,6 @@ static void event_new_fd(rad_listen_t *this) */ this->status = RAD_LISTEN_STATUS_REMOVE_NOW; } /* socket is at EOL */ -#endif /* WITH_TCP */ if (this->dead) goto wait_some_more; @@ -6147,7 +6256,7 @@ static void check_proxy(rad_listen_t *head) if (sock->my_ipaddr.af == AF_INET) has_v4 = true; if (sock->my_ipaddr.af == AF_INET6) has_v6 = true; break; - + default: break; } diff --git a/src/main/radclient.c b/src/main/radclient.c index 49da461..ab880dd 100644 --- a/src/main/radclient.c +++ b/src/main/radclient.c @@ -60,11 +60,13 @@ static fr_ipaddr_t server_ipaddr; static int resend_count = 1; static bool done = true; static bool print_filename = false; +static bool blast_radius = false; static fr_ipaddr_t client_ipaddr; static uint16_t client_port = 0; static int sockfd; +static int last_used_id = -1; #ifdef WITH_TCP static char const *proto = NULL; @@ -95,6 +97,7 @@ static void NEVER_RETURNS usage(void) fprintf(stderr, " <command> One of auth, acct, status, coa, disconnect or auto.\n"); fprintf(stderr, " -4 Use IPv4 address of server\n"); fprintf(stderr, " -6 Use IPv6 address of server.\n"); + fprintf(stderr, " -b Mandate checks for Blast RADIUS issue (this is not set by default).\n"); fprintf(stderr, " -c <count> Send each packet 'count' times.\n"); fprintf(stderr, " -d <raddb> Set user dictionary directory (defaults to " RADDBDIR ").\n"); fprintf(stderr, " -D <dictdir> Set main dictionary directory (defaults to " DICTDIR ").\n"); @@ -416,7 +419,7 @@ static int radclient_init(TALLOC_CTX *ctx, rc_file_pair_t *files) #endif request->files = files; - request->packet->id = -1; /* allocate when sending */ + request->packet->id = last_used_id; /* either requested, or allocated by the library */ request->num = num++; /* @@ -892,7 +895,7 @@ static int send_one_packet(rc_request_t *request) /* * Haven't sent the packet yet. Initialize it. */ - if (request->packet->id == -1) { + if (!request->tries || (request->packet->id == -1)) { int i; bool rcode; @@ -949,8 +952,18 @@ static int send_one_packet(rc_request_t *request) assert(request->packet->id != -1); assert(request->packet->data == NULL); - for (i = 0; i < 4; i++) { - ((uint32_t *) request->packet->vector)[i] = fr_rand(); + if (request->packet->code == PW_CODE_ACCESS_REQUEST) { + VALUE_PAIR *vp; + + if (((vp = fr_pair_find_by_num(request->packet->vps, PW_PACKET_AUTHENTICATION_VECTOR, 0, TAG_ANY)) != NULL) && + (vp->vp_length >= 16)) { + memcpy(request->packet->vector, vp->vp_octets, 16); + + } else { + for (i = 0; i < 4; i++) { + ((uint32_t *) request->packet->vector)[i] = fr_rand(); + } + } } /* @@ -1048,11 +1061,15 @@ static int send_one_packet(rc_request_t *request) REDEBUG("Failed to send packet for ID %d", request->packet->id); deallocate_id(request); request->done = true; + stats.lost++; return -1; } if (fr_log_fp) { fr_packet_header_print(fr_log_fp, request->packet, false); + + if (fr_debug_lvl > 2) rad_print_hex(request->packet); + if (fr_debug_lvl > 0) vp_printlist(fr_log_fp, request->packet->vps); } @@ -1060,6 +1077,130 @@ static int send_one_packet(rc_request_t *request) } /* + * Do Blast RADIUS checks. + * + * The request is an Access-Request, and does NOT contain Proxy-State. + * + * The reply is a raw packet, and is NOT yet decoded. + */ +static int blast_radius_check(rc_request_t *request, RADIUS_PACKET *reply) +{ + uint8_t *attr, *end; + VALUE_PAIR *vp; + bool have_message_authenticator = false; + + /* + * We've received a raw packet. Nothing has (as of yet) checked + * anything in it other than the length, and that it's a + * well-formed RADIUS packet. + */ + switch (reply->data[0]) { + case PW_CODE_ACCESS_ACCEPT: + case PW_CODE_ACCESS_REJECT: + case PW_CODE_ACCESS_CHALLENGE: + if (reply->data[1] != request->packet->id) { + ERROR("Invalid reply ID %d to Access-Request ID %d", reply->data[1], request->packet->id); + return -1; + } + break; + + default: + ERROR("Invalid reply code %d to Access-Request", reply->data[0]); + return -1; + } + + /* + * If the reply has a Message-Authenticator, then it MIGHT be fine. + */ + attr = reply->data + 20; + end = reply->data + reply->data_len; + + /* + * It should be the first attribute, so we warn if it isn't there. + * + * But it's not a fatal error. + */ + if (blast_radius && (attr[0] != PW_MESSAGE_AUTHENTICATOR)) { + RDEBUG("WARNING The %s reply packet does not have Message-Authenticator as the first attribute. The packet may be vulnerable to Blast RADIUS attacks.", + fr_packet_codes[reply->data[0]]); + } + + /* + * Set up for Proxy-State checks. + * + * If we see a Proxy-State in the reply which we didn't send, then it's a Blast RADIUS attack. + */ + vp = fr_pair_find_by_num(request->packet->vps, PW_PROXY_STATE, 0, TAG_ANY); + + while (attr < end) { + /* + * Blast RADIUS work-arounds require that + * Message-Authenticator is the first attribute in the + * reply. Note that we don't check for it being the + * first attribute, but simply that it exists. + * + * That check is a balance between securing the reply + * packet from attacks, and not violating the RFCs which + * say that there is no order to attributes in the + * packet. + * + * However, no matter the status of the '-b' flag we + * still can check for the signature of the attack, and + * discard packets which are suspicious. This behavior + * protects radclient from the attack, without mandating + * new behavior on the server side. + * + * Note that we don't set the '-b' flag by default. + * radclient is intended for testing / debugging, and is + * not intended to be used as part of a secure login / + * user checking system. + */ + if (attr[0] == PW_MESSAGE_AUTHENTICATOR) { + have_message_authenticator = true; + goto next; + } + + /* + * If there are Proxy-State attributes in the reply, they must + * match EXACTLY the Proxy-State attributes in the request. + * + * Note that we don't care if there are more Proxy-States + * in the request than in the reply. The Blast RADIUS + * issue requires _adding_ Proxy-State attributes, and + * cannot work when the server _deletes_ Proxy-State + * attributes. + */ + if (attr[0] == PW_PROXY_STATE) { + if (!vp || (vp->length != (size_t) (attr[1] - 2)) || (memcmp(vp->vp_octets, attr + 2, vp->length) != 0)) { + ERROR("Invalid reply to Access-Request ID %d - Discarding packet due to Blast RADIUS attack being detected.", request->packet->id); + ERROR("We received a Proxy-State in the reply which we did not send, or which is different from what we sent."); + return -1; + } + + vp = fr_pair_find_by_num(vp->next, PW_PROXY_STATE, 0, TAG_ANY); + } + + next: + attr += attr[1]; + } + + /* + * If "-b" is set, then we require Message-Authenticator in the reply. + */ + if (blast_radius && !have_message_authenticator) { + ERROR("The %s reply packet does not contain Message-Authenticator - discarding packet due to Blast RADIUS checks.", + fr_packet_codes[reply->data[0]]); + return -1; + } + + /* + * The packet doesn't look like it's a Blast RADIUS attack. The + * caller will now verify the packet signature. + */ + return 0; +} + +/* * Receive one packet, maybe. */ static int recv_one_packet(int wait_time) @@ -1101,6 +1242,8 @@ static int recv_one_packet(int wait_time) return -1; /* bad packet */ } + if (fr_debug_lvl > 2) rad_print_hex(reply); + packet_p = fr_packet_list_find_byreply(pl, reply); if (!packet_p) { ERROR("Received reply to request we did not send. (id=%d socket %d)", @@ -1111,6 +1254,20 @@ static int recv_one_packet(int wait_time) request = fr_packet2myptr(rc_request_t, packet, packet_p); /* + * We want radclient to be able to send any packet, including + * imperfect ones. However, we do NOT want to be vulnerable to + * the "Blast RADIUS" issue. Instead of adding command-line + * flags to enable/disable similar flags to what the server + * sends, we just do a few more smart checks to double-check + * things. + */ + if ((request->packet->code == PW_CODE_ACCESS_REQUEST) && + blast_radius_check(request, reply) < 0) { + rad_free(&reply); + return -1; + } + + /* * Fails the signature validation: not a real reply. * FIXME: Silently drop it and listen for another packet. */ @@ -1243,7 +1400,7 @@ int main(int argc, char **argv) exit(1); } - while ((c = getopt(argc, argv, "46c:d:D:f:Fhn:p:qr:sS:t:vx" + while ((c = getopt(argc, argv, "46bc:d:D:f:Fhi:n:p:qr:sS:t:vx" #ifdef WITH_TCP "P:" #endif @@ -1256,6 +1413,10 @@ int main(int argc, char **argv) force_af = AF_INET6; break; + case 'b': + blast_radius = true; + break; + case 'c': if (!isdigit((uint8_t) *optarg)) usage(); @@ -1297,6 +1458,15 @@ int main(int argc, char **argv) print_filename = true; break; + case 'i': + if (!isdigit((uint8_t) *optarg)) + usage(); + last_used_id = atoi(optarg); + if ((last_used_id < 0) || (last_used_id > 255)) { + usage(); + } + break; + case 'n': persec = atoi(optarg); if (persec <= 0) usage(); @@ -1562,6 +1732,7 @@ int main(int argc, char **argv) int n = parallel; rc_request_t *next; char const *filename = NULL; + time_t wake = 0; done = true; sleep_time = -1; @@ -1569,6 +1740,15 @@ int main(int argc, char **argv) /* * Walk over the packets, sending them. */ + for (this = request_head; this != NULL; this = this->next) { + if (this->reply) continue; + + if (!this->timestamp) continue; + + if (!wake || (wake > (this->timestamp + ((int) timeout) * (retries - this->tries)))) { + wake = this->timestamp + ((int) timeout) * (retries - this->tries); + } + } for (this = request_head; this != NULL; this = next) { next = this->next; @@ -1613,6 +1793,10 @@ int main(int argc, char **argv) break; } + if (!wake || (wake > (this->timestamp + ((int) timeout) * (retries - this->tries)))) { + wake = this->timestamp + ((int) timeout) * (retries - this->tries); + } + /* * Wait a little before sending * the next packet, if told to. @@ -1664,7 +1848,18 @@ int main(int argc, char **argv) * Still have outstanding requests. */ if (fr_packet_list_num_elements(pl) > 0) { + time_t now = time(NULL); done = false; + + /* + * The last time we wake up for a packet. + * + * If we're past that time, then give up. + */ + if (wake < now) { + break; + } + } else { sleep_time = 0; } diff --git a/src/main/radsecret b/src/main/radsecret new file mode 100755 index 0000000..2a03a2e --- /dev/null +++ b/src/main/radsecret @@ -0,0 +1,7 @@ +#!/usr/bin/env perl +# +# A tool which generates strong shared secrets. +# +use Convert::Base32; +use Crypt::URandom(); +print join('-', unpack("(A4)*", lc encode_base32(Crypt::URandom::urandom(12)))), "\n"; diff --git a/src/main/radsecret.mk b/src/main/radsecret.mk new file mode 100644 index 0000000..c5f43b4 --- /dev/null +++ b/src/main/radsecret.mk @@ -0,0 +1,5 @@ +install: $(R)/$(bindir)/radsecret + +$(R)/$(bindir)/radsecret: ${top_srcdir}/src/main/radsecret + @$(ECHO) INSTALL radsecret + $(Q)${PROGRAM_INSTALL} -c -m 755 $< $@ diff --git a/src/main/radsniff.c b/src/main/radsniff.c index e0d2b65..0458d77 100644 --- a/src/main/radsniff.c +++ b/src/main/radsniff.c @@ -1804,6 +1804,15 @@ static void _unmark_link(void *request) this->in_link_tree = false; } +/** Exit the event loop after a given timeout. + * + */ +static void timeout_event(UNUSED void *ctx) +{ + fr_event_loop_exit(events, 1); +} + + #ifdef HAVE_COLLECTDC_H /** Re-open the collectd socket * @@ -1919,6 +1928,7 @@ static void NEVER_RETURNS usage(int status) fprintf(output, " -R <filter> RADIUS attribute response filter.\n"); fprintf(output, " -s <secret> RADIUS secret.\n"); fprintf(output, " -S Write PCAP data to stdout.\n"); + fprintf(output, " -t <timeout> Stop after <timeout> seconds.\n"); fprintf(output, " -v Show program version information.\n"); fprintf(output, " -w <file> Write output packets to file.\n"); fprintf(output, " -x Print more debugging information.\n"); @@ -1947,6 +1957,8 @@ int main(int argc, char *argv[]) char buffer[1024]; int opt; + unsigned int timeout = 0; + fr_event_t *timeout_ev = NULL; char const *radius_dir = RADDBDIR; char const *dict_dir = DICTDIR; @@ -1999,7 +2011,7 @@ int main(int argc, char *argv[]) /* * Get options */ - while ((opt = getopt(argc, argv, "ab:c:Cd:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:Svw:xXW:T:P:N:O:")) != EOF) { + while ((opt = getopt(argc, argv, "ab:c:Cd:D:e:Ff:hi:I:l:L:mp:P:qr:R:s:St:vw:xXW:T:P:N:O:")) != EOF) { switch (opt) { case 'a': { @@ -2120,6 +2132,10 @@ int main(int argc, char *argv[]) conf->radius_secret = optarg; break; + case 't': + timeout = atoi(optarg); + break; + case 'S': conf->to_stdout = true; break; @@ -2569,7 +2585,7 @@ int main(int argc, char *argv[]) * Setup and enter the main event loop. Who needs libev when you can roll your own... */ { - struct timeval now; + struct timeval now, when; rs_update_t update; char *buff; @@ -2632,17 +2648,26 @@ int main(int argc, char *argv[]) update.stats = &stats; update.in = in; - now.tv_sec += conf->stats.interval; - now.tv_usec = 0; - if (!fr_event_insert(events, rs_stats_process, (void *) &update, &now, &event)) { + when = now; + when.tv_sec += conf->stats.interval; + when.tv_usec = 0; + if (!fr_event_insert(events, rs_stats_process, (void *) &update, &when, &event)) { ERROR("Failed inserting stats event"); } INFO("Muting stats for the next %i milliseconds (warmup)", conf->stats.timeout); - rs_tv_add_ms(&now, conf->stats.timeout, &stats.quiet); + rs_tv_add_ms(&when, conf->stats.timeout, &stats.quiet); } - } + if (timeout) { + when = now; + when.tv_sec += timeout; + + if (!fr_event_insert(events, timeout_event, NULL, &when, &timeout_ev)) { + ERROR("Failed inserting timeout event"); + } + } + } /* * Do this as late as possible so we can return an error code if something went wrong. diff --git a/src/main/realms.c b/src/main/realms.c index 2959d82..fa42813 100644 --- a/src/main/realms.c +++ b/src/main/realms.c @@ -452,6 +452,12 @@ static CONF_PARSER limit_config[] = { { "max_requests", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.max_requests), "0" }, { "lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.lifetime), "0" }, { "idle_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.idle_timeout), "0" }, +#ifdef SO_RCVTIMEO + { "read_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.read_timeout), NULL }, +#endif +#ifdef SO_SNDTIMEO + { "write_timeout", FR_CONF_OFFSET(PW_TYPE_INTEGER, home_server_t, limit.write_timeout), NULL }, +#endif CONF_PARSER_TERMINATOR }; @@ -475,8 +481,11 @@ static CONF_PARSER home_server_recv_coa[] = { #endif +static const char *require_message_authenticator = NULL; + static CONF_PARSER home_server_config[] = { { "nonblock", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, home_server_t, nonblock), "no" }, + { "require_message_authenticator", FR_CONF_POINTER(PW_TYPE_STRING| PW_TYPE_IGNORE_DEFAULT, &require_message_authenticator), NULL }, { "ipaddr", FR_CONF_OFFSET(PW_TYPE_COMBO_IP_ADDR, home_server_t, ipaddr), NULL }, { "ipv4addr", FR_CONF_OFFSET(PW_TYPE_IPV4_ADDR, home_server_t, ipaddr), NULL }, { "ipv6addr", FR_CONF_OFFSET(PW_TYPE_IPV6_ADDR, home_server_t, ipaddr), NULL }, @@ -780,6 +789,9 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE home->cs = cs; home->state = HOME_STATE_UNKNOWN; home->proto = IPPROTO_UDP; + home->require_ma = main_config.require_ma; + + require_message_authenticator = false; /* * Parse the configuration into the home server @@ -787,6 +799,10 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE */ if (cf_section_parse(cs, home, home_server_config) < 0) goto error; + if (fr_bool_auto_parse(cf_pair_find(cs, "require_message_authenticator"), &home->require_ma, require_message_authenticator) < 0) { + goto error; + } + /* * It has an IP address, it must be a remote server. */ @@ -1116,11 +1132,18 @@ home_server_t *home_server_afrom_cs(TALLOC_CTX *ctx, realm_config_t *rc, CONF_SE if (tls) { int rcode; + /* + * We don't require this for TLS connections. + */ + home->require_ma = false; + home->tls = tls_client_conf_parse(tls); if (!home->tls) { goto error; } + home->tls->name = "RADIUS/TLS"; + /* * Connection timeouts for outgoing TLS connections. */ @@ -3181,7 +3204,7 @@ int home_server_afrom_file(char const *filename) goto error; } -#ifdef COA_TUNNEL +#ifdef WITH_COA_TUNNEL if (home->recv_coa) { fr_strerror_printf("Dynamic home_server '%s' cannot receive CoA requests'", p); talloc_free(home); diff --git a/src/main/state.c b/src/main/state.c index 3700062..ab7a180 100644 --- a/src/main/state.c +++ b/src/main/state.c @@ -33,13 +33,14 @@ RCSID("$Id$") #include <freeradius-devel/process.h> typedef struct state_entry_t { - uint8_t state[AUTH_VECTOR_LEN]; + uint8_t state[MD5_DIGEST_LENGTH]; time_t cleanup; struct state_entry_t *prev; struct state_entry_t *next; int tries; + bool ours; TALLOC_CTX *ctx; VALUE_PAIR *vps; @@ -379,10 +380,55 @@ static void fr_state_cleanup(state_entry_t *head) request_inject(request); } + if (entry->opaque) { + entry->free_opaque(entry->opaque); + } + + if (entry->ctx) talloc_free(entry->ctx); + talloc_free(entry); } } +static void state_entry_calc(REQUEST *request, state_entry_t *entry, VALUE_PAIR *vp) +{ + /* + * Assume our own State first. This is where the state + * is the correct size, AND we're not proxying it to an + * external home server. If we are proxying it to an + * external home server, then that home server creates + * the State attribute, and we don't control it. + */ + if (entry->ours || + (vp->vp_length == sizeof(entry->state) && + (!request->proxy || (request->proxy->dst_port == 0)))) { + memcpy(entry->state, vp->vp_octets, sizeof(entry->state)); + entry->ours = true; + + } else { + FR_MD5_CTX ctx; + + /* + * We don't control the external State attribute. + * As a result, different home servers _may_ + * create the same State attribute. In order to + * differentiate them, we "mix in" the User-Name, + * which should contain the realm. And we then + * hope that different home servers in the same + * realm don't create overlapping State + * attributes. + */ + fr_md5_init(&ctx); + fr_md5_update(&ctx, vp->vp_octets, vp->vp_length); + + vp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); + if (vp) fr_md5_update(&ctx, vp->vp_octets, vp->vp_length); + + fr_md5_final(entry->state, &ctx); + fr_md5_destroy(&ctx); + } +} + /* * Create a new entry. Called with the mutex held. @@ -430,17 +476,18 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request, */ if (old) { entry->tries = old->tries + 1; + entry->ours = old->ours; /* * Track State */ - if (!vp) { + if (!vp && entry->ours) { memcpy(entry->state, old->state, sizeof(entry->state)); entry->state[1] = entry->state[0] ^ entry->tries; - entry->state[8] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 16) & 0xff); + entry->state[8] = entry->state[2] ^ (((uint32_t) HEXIFY(RADIUSD_VERSION)) & 0xff); entry->state[10] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 8) & 0xff); - entry->state[12] = entry->state[2] ^ (((uint32_t) HEXIFY(RADIUSD_VERSION)) & 0xff); + entry->state[12] = entry->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 16) & 0xff); } /* @@ -457,6 +504,8 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request, x = fr_rand(); memcpy(entry->state + (i * 4), &x, sizeof(x)); } + + entry->ours = true; /* we created it */ } /* @@ -464,27 +513,8 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request, * one we created above. */ if (vp) { - /* - * Assume our own State first. - */ - if (vp->vp_length == sizeof(entry->state)) { - memcpy(entry->state, vp->vp_octets, sizeof(entry->state)); + state_entry_calc(request, entry, vp); - /* - * Too big? Get the MD5 hash, in order - * to depend on the entire contents of State. - */ - } else if (vp->vp_length > sizeof(entry->state)) { - fr_md5_calc(entry->state, vp->vp_octets, vp->vp_length); - - /* - * Too small? Use the whole thing, and - * set the rest of entry->state to zero. - */ - } else { - memcpy(entry->state, vp->vp_octets, vp->vp_length); - memset(&entry->state[vp->vp_length], 0, sizeof(entry->state) - vp->vp_length); - } } else { vp = fr_pair_afrom_num(packet, PW_STATE, 0); fr_pair_value_memcpy(vp, entry->state, sizeof(entry->state)); @@ -497,7 +527,7 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request, /* * Make unique for different virtual servers handling same request */ - *((uint32_t *)(&entry->state[4])) ^= fr_hash_string(request->server); + if (entry->ours) *((uint32_t *)(&entry->state[4])) ^= fr_hash_string(request->server); /* * Copy server to state in case it's needed for cleanup @@ -520,7 +550,7 @@ static state_entry_t *fr_state_entry_create(fr_state_t *state, REQUEST *request, /* * Find the entry, based on the State attribute. */ -static state_entry_t *fr_state_find(fr_state_t *state, const char *server, RADIUS_PACKET *packet) +static state_entry_t *fr_state_find(REQUEST *request, fr_state_t *state, const char *server, RADIUS_PACKET *packet) { VALUE_PAIR *vp; state_entry_t *entry, my_entry; @@ -528,31 +558,12 @@ static state_entry_t *fr_state_find(fr_state_t *state, const char *server, RADIU vp = fr_pair_find_by_num(packet->vps, PW_STATE, 0, TAG_ANY); if (!vp) return NULL; - /* - * Assume our own State first. - */ - if (vp->vp_length == sizeof(my_entry.state)) { - memcpy(my_entry.state, vp->vp_octets, sizeof(my_entry.state)); - - /* - * Too big? Get the MD5 hash, in order - * to depend on the entire contents of State. - */ - } else if (vp->vp_length > sizeof(my_entry.state)) { - fr_md5_calc(my_entry.state, vp->vp_octets, vp->vp_length); - - /* - * Too small? Use the whole thing, and - * set the rest of my_entry.state to zero. - */ - } else { - memcpy(my_entry.state, vp->vp_octets, vp->vp_length); - memset(&my_entry.state[vp->vp_length], 0, sizeof(my_entry.state) - vp->vp_length); - } + my_entry.ours = false; + state_entry_calc(request, &my_entry, vp); /* Make unique for different virtual servers handling same request */ - if (server) *((uint32_t *)(&my_entry.state[4])) ^= fr_hash_string(server); + if (server && my_entry.ours) *((uint32_t *)(&my_entry.state[4])) ^= fr_hash_string(server); entry = rbtree_finddata(state->tree, &my_entry); @@ -576,7 +587,7 @@ void fr_state_discard(REQUEST *request, RADIUS_PACKET *original) request->state = NULL; PTHREAD_MUTEX_LOCK(&state->mutex); - entry = fr_state_find(state, request->server, original); + entry = fr_state_find(request, state, request->server, original); if (entry) state_entry_free(state, entry); PTHREAD_MUTEX_UNLOCK(&state->mutex); } @@ -601,7 +612,7 @@ void fr_state_get_vps(REQUEST *request, RADIUS_PACKET *packet) rad_assert(request->state == NULL); PTHREAD_MUTEX_LOCK(&state->mutex); - entry = fr_state_find(state, request->server, packet); + entry = fr_state_find(request, state, request->server, packet); /* * This has to be done in a mutex lock, because talloc @@ -683,7 +694,7 @@ bool fr_state_put_vps(REQUEST *request, RADIUS_PACKET *original, RADIUS_PACKET * cleanup_list = fr_state_cleanup_find(state); if (original) { - old = fr_state_find(state, request->server, original); + old = fr_state_find(request, state, request->server, original); } else { old = NULL; } diff --git a/src/main/stats.c b/src/main/stats.c index a5c672e..29f2c48 100644 --- a/src/main/stats.c +++ b/src/main/stats.c @@ -91,51 +91,57 @@ static void stats_time(fr_stats_t *stats, struct timeval *start, void request_stats_final(REQUEST *request) { rad_listen_t *listener; + RADCLIENT *client; - if (request->master_state == REQUEST_COUNTED) return; + if ((request->options & RAD_REQUEST_OPTION_STATS) != 0) return; - if (!request->listener) return; - if (!request->client) return; + /* don't count statistic requests */ + if (request->packet->code == PW_CODE_STATUS_SERVER) { + return; + } - if ((request->listener->type != RAD_LISTEN_NONE) && + listener = request->listener; + if (listener) switch (listener->type) { + case RAD_LISTEN_NONE: #ifdef WITH_ACCOUNTING - (request->listener->type != RAD_LISTEN_ACCT) && + case RAD_LISTEN_ACCT: #endif #ifdef WITH_COA - (request->listener->type != RAD_LISTEN_COA) && + case RAD_LISTEN_COA: #endif - (request->listener->type != RAD_LISTEN_AUTH)) return; + case RAD_LISTEN_AUTH: + break; - /* don't count statistic requests */ - if (request->packet->code == PW_CODE_STATUS_SERVER) - return; + default: + return; + } /* * Deal with TCP / TLS issues. The statistics are kept in the parent socket. */ - listener = request->listener; - if (listener->parent) listener = listener->parent; + if (listener && listener->parent) listener = listener->parent; + client = request->client; #undef INC_AUTH -#define INC_AUTH(_x) radius_auth_stats._x++;listener->stats._x++;request->client->auth._x++; +#define INC_AUTH(_x) radius_auth_stats._x++;if (listener) listener->stats._x++;if (client) client->auth._x++; #undef INC_ACCT #ifdef WITH_ACCOUNTING -#define INC_ACCT(_x) radius_acct_stats._x++;listener->stats._x++;request->client->acct._x++ +#define INC_ACCT(_x) radius_acct_stats._x++;if (listener) listener->stats._x++;if (client) client->acct._x++ #else #define INC_ACCT(_x) #endif #undef INC_COA #ifdef WITH_COA -#define INC_COA(_x) radius_coa_stats._x++;listener->stats._x++;request->client->coa._x++ +#define INC_COA(_x) radius_coa_stats._x++;if (listener) listener->stats._x++;if (client) client->coa._x++ #else #define INC_COA(_x) #endif #undef INC_DSC #ifdef WITH_DSC -#define INC_DSC(_x) radius_dsc_stats._x++;listener->stats._x++;request->client->dsc._x++ +#define INC_DSC(_x) radius_dsc_stats._x++;if (listener) listener->stats._x++;if (client) client->dsc._x++ #else #define INC_DSC(_x) #endif @@ -148,7 +154,7 @@ void request_stats_final(REQUEST *request) * deleted, because only the main server thread calls * this function, which makes it thread-safe. */ - if (request->reply && (request->packet->code != PW_CODE_STATUS_SERVER)) switch (request->reply->code) { + if (request->reply) switch (request->reply->code) { case PW_CODE_ACCESS_ACCEPT: INC_AUTH(total_access_accepts); @@ -247,25 +253,21 @@ void request_stats_final(REQUEST *request) switch (request->proxy->code) { case PW_CODE_ACCESS_REQUEST: proxy_auth_stats.total_requests += request->num_proxied_requests; - request->home_server->stats.total_requests += request->num_proxied_requests; break; #ifdef WITH_ACCOUNTING case PW_CODE_ACCOUNTING_REQUEST: proxy_acct_stats.total_requests += request->num_proxied_requests; - request->home_server->stats.total_requests += request->num_proxied_requests; break; #endif #ifdef WITH_COA case PW_CODE_COA_REQUEST: proxy_coa_stats.total_requests += request->num_proxied_requests; - request->home_server->stats.total_requests += request->num_proxied_requests; break; case PW_CODE_DISCONNECT_REQUEST: proxy_dsc_stats.total_requests += request->num_proxied_requests; - request->home_server->stats.total_requests += request->num_proxied_requests; break; #endif @@ -276,7 +278,7 @@ void request_stats_final(REQUEST *request) if (!request->proxy_reply) goto done; /* simplifies formatting */ #undef INC -#define INC(_x) proxy_auth_stats._x += request->num_proxied_responses; request->home_server->stats._x += request->num_proxied_responses; +#define INC(_x) proxy_auth_stats._x += request->num_proxied_responses;request->home_server->stats._x += request->num_proxied_responses; switch (request->proxy_reply->code) { case PW_CODE_ACCESS_ACCEPT: @@ -347,10 +349,7 @@ void request_stats_final(REQUEST *request) done: #endif /* WITH_PROXY */ - if (request->max_time) { - RADCLIENT *client = request->client; - switch (request->packet->code) { case PW_CODE_ACCESS_REQUEST: FR_STATS_INC(auth, unresponsive_child); @@ -376,7 +375,7 @@ void request_stats_final(REQUEST *request) } } - request->master_state = REQUEST_COUNTED; + request->options |= RAD_REQUEST_OPTION_STATS; } typedef struct fr_stats2vp { @@ -525,8 +524,8 @@ void request_stats_reply(REQUEST *request) /* * Authentication. */ - if (((flag->vp_integer & 0x01) != 0) && - ((flag->vp_integer & 0xc0) == 0)) { + if (((flag->vp_integer & 0x01) != 0) && /* auth */ + ((flag->vp_integer & 0xe0) == 0)) { /* not client, server or home-server */ request_stats_addvp(request, authvp, &radius_auth_stats); } @@ -534,8 +533,8 @@ void request_stats_reply(REQUEST *request) /* * Accounting */ - if (((flag->vp_integer & 0x02) != 0) && - ((flag->vp_integer & 0xc0) == 0)) { + if (((flag->vp_integer & 0x02) != 0) && /* accounting */ + ((flag->vp_integer & 0xe0) == 0)) { /* not client, server or home-server */ request_stats_addvp(request, acctvp, &radius_acct_stats); } #endif @@ -544,8 +543,8 @@ void request_stats_reply(REQUEST *request) /* * Proxied authentication requests. */ - if (((flag->vp_integer & 0x04) != 0) && - ((flag->vp_integer & 0x20) == 0)) { + if (((flag->vp_integer & 0x04) != 0) && /* proxy-auth */ + ((flag->vp_integer & 0x20) == 0)) { /* not client */ request_stats_addvp(request, proxy_authvp, &proxy_auth_stats); } @@ -553,8 +552,8 @@ void request_stats_reply(REQUEST *request) /* * Proxied accounting requests. */ - if (((flag->vp_integer & 0x08) != 0) && - ((flag->vp_integer & 0x20) == 0)) { + if (((flag->vp_integer & 0x08) != 0) && /* proxy-accounting */ + ((flag->vp_integer & 0x20) == 0)) { /* not client */ request_stats_addvp(request, proxy_acctvp, &proxy_acct_stats); } #endif @@ -563,7 +562,7 @@ void request_stats_reply(REQUEST *request) /* * Internal server statistics */ - if ((flag->vp_integer & 0x10) != 0) { + if ((flag->vp_integer & 0x10) != 0) { /* internal */ vp = radius_pair_create(request->reply, &request->reply->vps, PW_FREERADIUS_STATS_START_TIME, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = start_time.tv_sec; @@ -607,7 +606,7 @@ void request_stats_reply(REQUEST *request) /* * For a particular client. */ - if ((flag->vp_integer & 0x20) != 0) { + if ((flag->vp_integer & 0x20) != 0) { /* client */ fr_ipaddr_t ipaddr; VALUE_PAIR *server_ip, *server_port = NULL; RADCLIENT *client = NULL; @@ -764,8 +763,8 @@ void request_stats_reply(REQUEST *request) /* * For a particular "listen" socket. */ - if (((flag->vp_integer & 0x40) != 0) && - ((flag->vp_integer & 0x03) != 0)) { + if (((flag->vp_integer & 0x40) != 0) && /* server */ + ((flag->vp_integer & 0x03) != 0)) { /* auth or accounting */ rad_listen_t *this; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; @@ -807,7 +806,7 @@ void request_stats_reply(REQUEST *request) fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, server_port)); - if ((flag->vp_integer & 0x01) != 0) { + if ((flag->vp_integer & 0x01) != 0) { /* auth */ if ((request->listener->type == RAD_LISTEN_AUTH) || (request->listener->type == RAD_LISTEN_NONE)) { request_stats_addvp(request, authvp, &this->stats); @@ -817,7 +816,7 @@ void request_stats_reply(REQUEST *request) } #ifdef WITH_ACCOUNTING - if ((flag->vp_integer & 0x02) != 0) { + if ((flag->vp_integer & 0x02) != 0) { /* accounting */ if ((request->listener->type == RAD_LISTEN_ACCT) || (request->listener->type == RAD_LISTEN_NONE)) { request_stats_addvp(request, acctvp, &this->stats); @@ -832,8 +831,8 @@ void request_stats_reply(REQUEST *request) /* * Home servers. */ - if (((flag->vp_integer & 0x80) != 0) && - ((flag->vp_integer & 0x03) != 0)) { + if (((flag->vp_integer & 0x80) != 0) && /* home-server */ + ((flag->vp_integer & 0x03) != 0)) { /* auth or accounting */ home_server_t *home; VALUE_PAIR *server_ip, *server_port; fr_ipaddr_t ipaddr; @@ -935,7 +934,7 @@ void request_stats_reply(REQUEST *request) PW_FREERADIUS_STATS_LAST_PACKET_SENT, VENDORPEC_FREERADIUS); if (vp) vp->vp_date = home->last_packet_sent; - if ((flag->vp_integer & 0x01) != 0) { + if ((flag->vp_integer & 0x01) != 0) { /* auth */ if (home->type == HOME_TYPE_AUTH) { request_stats_addvp(request, proxy_authvp, &home->stats); @@ -945,7 +944,7 @@ void request_stats_reply(REQUEST *request) } #ifdef WITH_ACCOUNTING - if ((flag->vp_integer & 0x02) != 0) { + if ((flag->vp_integer & 0x02) != 0) { /* accounting */ if (home->type == HOME_TYPE_ACCT) { request_stats_addvp(request, proxy_acctvp, &home->stats); @@ -991,14 +990,14 @@ void radius_stats_ema(fr_stats_ema_t *ema, } - tdiff = start->tv_sec; - tdiff -= end->tv_sec; + tdiff = end->tv_sec; + tdiff -= start->tv_sec; micro = (int) tdiff; if (micro > 40) micro = 40; /* don't overflow 32-bit ints */ micro *= USEC; - micro += start->tv_usec; - micro -= end->tv_usec; + micro += end->tv_usec; + micro -= start->tv_usec; micro *= EMA_SCALE; diff --git a/src/main/threads.c b/src/main/threads.c index a187106..5730b5e 100644 --- a/src/main/threads.c +++ b/src/main/threads.c @@ -291,7 +291,7 @@ static void tls_mutexes_destroy(void) #ifdef HAVE_CRYPTO_SET_LOCKING_CALLBACK int i, num; - rad_assert(ssl_mutex != NULL); + rad_assert(ssl_mutexes != NULL); num = CRYPTO_num_locks(); diff --git a/src/main/tls.c b/src/main/tls.c index c8cae3b..736ee41 100644 --- a/src/main/tls.c +++ b/src/main/tls.c @@ -404,7 +404,7 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity, * The passed identity is weird. Deny it. */ if (!identity_is_safe(identity)) { - RWDEBUG("(TLS) Invalid characters in PSK identity %s", identity); + RWDEBUG("(TLS) %s - Invalid characters in PSK identity %s", conf->name, identity); return 0; } @@ -421,7 +421,7 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity, hex_len = radius_xlat(buffer, sizeof(buffer), request, conf->psk_query, NULL, NULL); if (!hex_len) { - RWDEBUG("(TLS) PSK expansion returned an empty string."); + RWDEBUG("(TLS) %s - PSK expansion returned an empty string.", conf->name); return 0; } @@ -431,7 +431,7 @@ static unsigned int psk_server_callback(SSL *ssl, const char *identity, * the truncation, and complain about it. */ if (hex_len > (2 * max_psk_len)) { - RWDEBUG("(TLS) Returned PSK is too long (%u > %u)", + RWDEBUG("(TLS) %s - Returned PSK is too long (%u > %u)", conf->name, (unsigned int) hex_len, 2 * max_psk_len); return 0; } @@ -635,9 +635,11 @@ tls_session_t *tls_new_client_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *con case SSL_ERROR_WANT_READ: ssn->connected = false; + RDEBUG("(TLS) %s - tls_new_client_session WANT_READ", conf->name); return ssn; case SSL_ERROR_WANT_WRITE: + RDEBUG("(TLS) %s - tls_new_client_session WANT_WRITE", conf->name); ssn->connected = false; return ssn; } @@ -681,7 +683,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU rad_assert(request != NULL); - RDEBUG2("(TLS) Initiating new session"); + RDEBUG2("(TLS) %s -Initiating new session", conf->name); /* * Replace X509 store if it is time to update CRLs/certs in ca_path @@ -690,10 +692,10 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU pthread_mutex_lock(&conf->mutex); /* recheck conf->ca_path_last_reload because it may be inaccurate without mutex */ if (conf->ca_path_last_reload + conf->ca_path_reload_interval <= request->timestamp) { - RDEBUG2("Flushing X509 store to re-read data from ca_path dir"); + RDEBUG2("(TLS) Flushing X509 store to re-read data from ca_path dir"); if ((new_cert_store = fr_init_x509_store(conf)) == NULL) { - RERROR("(TLS) Error replacing X509 store, out of memory (?)"); + RERROR("(TLS) %s - Error replacing X509 store, out of memory (?)", conf->name); } else { if (conf->old_x509_store) X509_STORE_free(conf->old_x509_store); /* @@ -752,7 +754,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU SSL_set_app_data(new_tls, NULL); if ((state = talloc_zero(ctx, tls_session_t)) == NULL) { - RERROR("(TLS) Error allocating memory for SSL state"); + RERROR("(TLS) %s - Error allocating memory for SSL state", conf->name); return NULL; } session_init(state); @@ -808,7 +810,7 @@ tls_session_t *tls_new_session(TALLOC_CTX *ctx, fr_tls_server_conf_t *conf, REQU VALUE_PAIR *key = fr_pair_find_by_num(request->config, PW_TLS_SESSION_CERT_PRIVATE_KEY_FILE, 0, TAG_ANY); if (!key) key = vp; - RDEBUG2("(TLS) Loading session certificate file \"%s\"", vp->vp_strvalue); + RDEBUG2("(TLS) %s - Loading session certificate file \"%s\"", conf->name, vp->vp_strvalue); if (conf->realms) { fr_realm_ctx_t my_r, *r; @@ -887,11 +889,18 @@ after_chain: * Verify the peer certificate, if asked. */ if (client_cert) { - RDEBUG2("(TLS) Setting verify mode to require certificate from client"); + RDEBUG2("(TLS) %s - Setting verify mode to require certificate from client", conf->name); verify_mode = SSL_VERIFY_PEER; verify_mode |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; verify_mode |= SSL_VERIFY_CLIENT_ONCE; } +#ifdef PSK_MAX_IDENTITY_LEN + else if (conf->psk_identity) { + RDEBUG2("(TLS) %s - Setting verify peer mode due to PSK", conf->name); + verify_mode = SSL_VERIFY_PEER; + verify_mode |= SSL_VERIFY_CLIENT_ONCE; + } +#endif SSL_set_verify(state->ssl, verify_mode, cbtls_verify); SSL_set_ex_data(state->ssl, FR_TLS_EX_INDEX_CONF, (void *)conf); @@ -970,14 +979,14 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn) int err; if (ssn->invalid_hb_used) { - REDEBUG("(TLS) OpenSSL Heartbeat attack detected. Closing connection"); + REDEBUG("(TLS) %s - OpenSSL Heartbeat attack detected. Closing connection", ssn->conf->name); return 0; } if (ssn->dirty_in.used > 0) { err = BIO_write(ssn->into_ssl, ssn->dirty_in.data, ssn->dirty_in.used); if (err != (int) ssn->dirty_in.used) { - REDEBUG("(TLS) Failed writing %zd bytes to SSL BIO: %d", ssn->dirty_in.used, err); + REDEBUG("(TLS) %s - Failed writing %zd bytes to SSL BIO: %d", ssn->conf->name, ssn->dirty_in.used, err); record_init(&ssn->dirty_in); return 0; } @@ -998,7 +1007,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn) VALUE_PAIR *vp; char const *str_version; - RDEBUG2("(TLS) Connection Established"); + RDEBUG2("(TLS) %s - Connection Established", ssn->conf->name); ssn->is_init_finished = true; vp = fr_pair_afrom_num(request->state_ctx, PW_TLS_SESSION_CIPHER_SUITE, 0); @@ -1049,10 +1058,10 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn) REXDENT(); } } - else if (SSL_in_init(ssn->ssl)) { RDEBUG2("(TLS) In Handshake Phase"); } - else if (SSL_in_before(ssn->ssl)) { RDEBUG2("(TLS) Before Handshake Phase"); } - else if (SSL_in_accept_init(ssn->ssl)) { RDEBUG2("(TLS) In Accept mode"); } - else if (SSL_in_connect_init(ssn->ssl)) { RDEBUG2("(TLS) In Connect mode"); } + else if (SSL_in_init(ssn->ssl)) { RDEBUG2("(TLS) %s - In Handshake Phase", ssn->conf->name); } + else if (SSL_in_before(ssn->ssl)) { RDEBUG2("(TLS) %s - Before Handshake Phase", ssn->conf->name); } + else if (SSL_in_accept_init(ssn->ssl)) { RDEBUG2("(TLS) %s- In Accept mode", ssn->conf->name); } + else if (SSL_in_connect_init(ssn->ssl)) { RDEBUG2("(TLS) %s - In Connect mode", ssn->conf->name); } #if OPENSSL_VERSION_NUMBER >= 0x10001000L /* @@ -1070,7 +1079,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn) * to get the session is a hard fail. */ if (!ssn->ssl_session && ssn->is_init_finished) { - RDEBUG("(TLS) Failed getting session"); + RDEBUG("(TLS) %s - Failed getting session", ssn->conf->name); return 0; } } @@ -1084,12 +1093,12 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn) err = BIO_read(ssn->from_ssl, ssn->dirty_out.data, sizeof(ssn->dirty_out.data)); if (err > 0) { - RDEBUG3("(TLS) got %d bytes of data", err); + RDEBUG3("(TLS) %s- got %d bytes of data", ssn->conf->name, err); ssn->dirty_out.used = err; } else if (BIO_should_retry(ssn->from_ssl)) { record_init(&ssn->dirty_in); - RDEBUG2("(TLS) Asking for more data in tunnel."); + RDEBUG2("(TLS) %s - Asking for more data in tunnel.", ssn->conf->name); return 1; } else { @@ -1098,7 +1107,7 @@ int tls_handshake_recv(REQUEST *request, tls_session_t *ssn) return 0; } } else { - RDEBUG2("(TLS) Application data."); + RDEBUG2("(TLS) %s - Application data.", ssn->conf->name); /* Its clean application data, leave whatever is in the buffer */ #if 0 record_init(&ssn->clean_out); @@ -1245,6 +1254,7 @@ void tls_session_information(tls_session_t *tls_session) REQUEST *request; VALUE_PAIR *vp; char content_type[16], alert_buf[16]; + char name_buf[128]; char buffer[32]; /* @@ -1262,7 +1272,12 @@ void tls_session_information(tls_session_t *tls_session) request = SSL_get_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST); if (!request) return; - str_write_p = tls_session->info.origin ? "(TLS) send" : "(TLS) recv"; + if (tls_session->info.origin) { + snprintf(name_buf, sizeof(name_buf), "(TLS) %s - send", tls_session->conf->name); + } else { + snprintf(name_buf, sizeof(name_buf), "(TLS) %s - recv", tls_session->conf->name); + } + str_write_p = name_buf; #define FROM_CLIENT (tls_session->info.origin == 0) @@ -1605,7 +1620,7 @@ void tls_session_information(tls_session_t *tls_session) RDEBUG2("%s", tls_session->info.info_description); - if (FROM_CLIENT && details) RDEBUG2("(TLS) The client is informing us that %s.", details); + if (FROM_CLIENT && details) RDEBUG2("(TLS) %s - The client is informing us that %s.", tls_session->conf->name, details); } static CONF_PARSER cache_config[] = { @@ -1735,6 +1750,10 @@ static CONF_PARSER tls_client_config[] = { { "certificate_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, certificate_file), NULL }, { "ca_file", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, fr_tls_server_conf_t, ca_file), NULL }, { "private_key_password", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, private_key_password), NULL }, +#ifdef PSK_MAX_IDENTITY_LEN + { "psk_identity", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, psk_identity), NULL }, + { "psk_hexphrase", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_SECRET, fr_tls_server_conf_t, psk_password), NULL }, +#endif { "dh_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, dh_file), NULL }, { "random_file", FR_CONF_OFFSET(PW_TYPE_STRING, fr_tls_server_conf_t, random_file), NULL }, { "fragment_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, fr_tls_server_conf_t, fragment_size), "1024" }, @@ -1924,7 +1943,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess) blob_len = i2d_SSL_SESSION(sess, NULL); if (blob_len < 1) { /* something went wrong */ - if (request) RWDEBUG("(TLS) Session serialisation failed, could not determine required buffer length"); + if (request) RWDEBUG("(TLS) %s - Session serialisation failed, could not determine required buffer length", conf->name); return 0; } @@ -1932,14 +1951,14 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess) /* alloc and convert to ASN.1 */ sess_blob = malloc(blob_len); if (!sess_blob) { - RWDEBUG("(TLS) Session serialisation failed, couldn't allocate buffer (%d bytes)", blob_len); + RWDEBUG("(TLS) %s - Session serialisation failed, couldn't allocate buffer (%d bytes)", conf->name, blob_len); return 0; } /* openssl mutates &p */ p = sess_blob; rv = i2d_SSL_SESSION(sess, &p); if (rv != blob_len) { - if (request) RWDEBUG("(TLS) Session serialisation failed"); + if (request) RWDEBUG("(TLS) %s - Session serialisation failed", conf->name); goto error; } @@ -1948,8 +1967,8 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess) conf->session_cache_path, FR_DIR_SEP, buffer); fd = open(filename, O_RDWR|O_CREAT|O_EXCL, S_IWUSR); if (fd < 0) { - if (request) RERROR("(TLS) Session serialisation failed, failed opening session file %s: %s", - filename, fr_syserror(errno)); + if (request) RERROR("(TLS) %s - Session serialisation failed, failed opening session file %s: %s", + conf->name, filename, fr_syserror(errno)); goto error; } @@ -1971,7 +1990,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess) while (todo > 0) { rv = write(fd, p, todo); if (rv < 1) { - if (request) RWDEBUG("(TLS) Failed writing session: %s", fr_syserror(errno)); + if (request) RWDEBUG("(TLS) %s - Failed writing session: %s", conf->name, fr_syserror(errno)); close(fd); goto error; } @@ -1979,7 +1998,7 @@ static int cbtls_new_session(SSL *ssl, SSL_SESSION *sess) todo -= rv; } close(fd); - if (request) RWDEBUG("(TLS) Wrote session %s to %s (%d bytes)", buffer, filename, blob_len); + if (request) RWDEBUG("(TLS) %s - Wrote session %s to %s (%d bytes)", conf->name, buffer, filename, blob_len); } error: @@ -2102,20 +2121,20 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l snprintf(filename, sizeof(filename), "%s%c%s.asn1", conf->session_cache_path, FR_DIR_SEP, buffer); fd = open(filename, O_RDONLY); if (fd < 0) { - RWDEBUG("(TLS) No persisted session file %s: %s", filename, fr_syserror(errno)); + RWDEBUG("(TLS) %s - No persisted session file %s: %s", conf->name, filename, fr_syserror(errno)); goto error; } rv = fstat(fd, &st); if (rv < 0) { - RWDEBUG("(TLS) Failed stating persisted session file %s: %s", filename, fr_syserror(errno)); + RWDEBUG("(TLS) %s - Failed stating persisted session file %s: %s", conf->name, filename, fr_syserror(errno)); close(fd); goto error; } sess_data = talloc_array(NULL, unsigned char, st.st_size); if (!sess_data) { - RWDEBUG("(TLS) Failed allocating buffer for persisted session (%d bytes)", (int) st.st_size); + RWDEBUG("(TLS) %s- Failed allocating buffer for persisted session (%d bytes)", conf->name, (int) st.st_size); close(fd); goto error; } @@ -2125,7 +2144,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l while (todo > 0) { rv = read(fd, q, todo); if (rv < 1) { - RWDEBUG("(TLS) Failed reading persisted session: %s", fr_syserror(errno)); + RWDEBUG("(TLS) %s - Failed reading persisted session: %s", conf->name, fr_syserror(errno)); close(fd); goto error; } @@ -2149,7 +2168,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l memcpy(&o, &p, sizeof(o)); sess = d2i_SSL_SESSION(NULL, o, st.st_size); if (!sess) { - RWDEBUG("(TLS) Failed loading persisted session: %s", ERR_error_string(ERR_get_error(), NULL)); + RWDEBUG("(TLS) %s - Failed loading persisted session: %s", conf->name, ERR_error_string(ERR_get_error(), NULL)); goto error; } @@ -2159,7 +2178,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l rv = pairlist_read(talloc_ctx, filename, &pairlist, 1); if (rv < 0) { /* not safe to un-persist a session w/o VPs */ - RWDEBUG("(TLS) Failed loading persisted VPs for session %s", buffer); + RWDEBUG("(TLS) %s - Failed loading persisted VPs for session %s", conf->name, buffer); SSL_SESSION_free(sess); sess = NULL; goto error; @@ -2173,7 +2192,7 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l time_t expires; if (ocsp_asn1time_to_epoch(&expires, vp->vp_strvalue) < 0) { - RDEBUG2("Failed getting certificate expiration, removing cache entry for session %s - %s", buffer, fr_strerror()); + RDEBUG2("(TLS) %s - Failed getting certificate expiration, removing cache entry for session %s - %s", conf->name, buffer, fr_strerror()); SSL_SESSION_free(sess); sess = NULL; goto error; @@ -2193,8 +2212,8 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l if (vp) { if ((request->timestamp + vp->vp_integer) > expires) { vp->vp_integer = expires - request->timestamp; - RWDEBUG2("(TLS) Updating Session-Timeout to %u, due to impending certificate expiration", - vp->vp_integer); + RWDEBUG2("(TLS) %s - Updating Session-Timeout to %u, due to impending certificate expiration", + conf->name, vp->vp_integer); } } } @@ -2208,8 +2227,8 @@ static SSL_SESSION *cbtls_get_session(SSL *ssl, const unsigned char *data, int l VALUE_PAIR *type = fr_pair_find_by_num(request->packet->vps, PW_EAP_TYPE, 0, TAG_ANY); if (type && (type->vp_integer != vp->vp_integer)) { - REDEBUG("Resumption has changed EAP types for session %s", buffer); - REDEBUG("Rejecting session due to protocol violations"); + REDEBUG("(TLS) %s - Resumption has changed EAP types for session %s", conf->name, buffer); + REDEBUG("(TLS) %s - Rejecting session due to protocol violations", conf->name); goto error; } } @@ -2524,8 +2543,8 @@ static SSL_SESSION *cbtls_cache_load(SSL *ssl, const unsigned char *data, int le if (vp) { if ((request->timestamp + vp->vp_integer) > expires) { vp->vp_integer = expires - request->timestamp; - RWDEBUG2("(TLS) Updating Session-Timeout to %u, due to impending certificate expiration", - vp->vp_integer); + RWDEBUG2("(TLS) %s - Updating Session-Timeout to %u, due to impending certificate expiration", + conf->name, vp->vp_integer); } } } @@ -2535,7 +2554,7 @@ static SSL_SESSION *cbtls_cache_load(SSL *ssl, const unsigned char *data, int le */ vp = fr_pair_find_by_num(fake->state, PW_TLS_SESSION_DATA, 0, TAG_ANY); if (!vp) { - RWDEBUG("(TLS) Failed to find TLS-Session-Data in 'session-state' list for session %s", buffer); + RWDEBUG("(TLS) %s - Failed to find TLS-Session-Data in 'session-state' list for session %s", conf->name, buffer); goto error; } @@ -2552,7 +2571,7 @@ static SSL_SESSION *cbtls_cache_load(SSL *ssl, const unsigned char *data, int le p = vp->vp_octets; sess = d2i_SSL_SESSION(NULL, &p, vp->vp_length); if (!sess) { - RWDEBUG("(TLS) Failed loading persisted session: %s", ERR_error_string(ERR_get_error(), NULL)); + RWDEBUG("(TLS) %s - Failed loading persisted session: %s", conf->name, ERR_error_string(ERR_get_error(), NULL)); goto error; } @@ -2629,7 +2648,7 @@ typedef enum { } ocsp_status_t; static ocsp_status_t ocsp_check(REQUEST *request, X509_STORE *store, X509 *issuer_cert, X509 *client_cert, - fr_tls_server_conf_t *conf) + STACK_OF(X509) *untrusted, fr_tls_server_conf_t *conf) { OCSP_CERTID *certid; OCSP_REQUEST *req; @@ -2810,7 +2829,7 @@ static ocsp_status_t ocsp_check(REQUEST *request, X509_STORE *store, X509 *issue REDEBUG("ocsp: Response has wrong nonce value"); goto ocsp_end; } - if (OCSP_basic_verify(bresp, NULL, store, 0)!=1){ + if (OCSP_basic_verify(bresp, untrusted, store, 0)!=1){ REDEBUG("ocsp: Couldn't verify OCSP basic response"); goto ocsp_end; } @@ -2931,10 +2950,6 @@ static char const *cert_attr_names[9][2] = { #define FR_TLS_SAN_UPN (7) #define FR_TLS_VALID_SINCE (8) -static const char *cert_names[2] = { - "client", "server", -}; - /* * Before trusting a certificate, you must make sure that the * certificate is 'valid'. There are several steps that your @@ -3048,7 +3063,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx) buf[0] = '\0'; sn = X509_get_serialNumber(client_cert); - RDEBUG2("(TLS) Creating attributes from %s certificate", cert_names[lookup ]); + RDEBUG2("(TLS) %s - Creating attributes from %d certificate in chain", conf->name, lookup + 1); RINDENT(); /* @@ -3340,8 +3355,8 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx) #if 0 ASN1_TIME_print(bio_err, X509_get_notAfter(ctx->current_cert)); -#endif break; +#endif } /* @@ -3366,8 +3381,10 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx) if (conf->disallow_untrusted || RDEBUG_ENABLED2) { int i; - WARN("Certificate chain - %i cert(s) untrusted", + WARN("Certificate chain - %i intermediate CA cert(s) untrusted", X509_STORE_CTX_get_num_untrusted(ctx)); + WARN("To forbid these certificates see 'reject_unknown_intermediate_ca'"); + for (i = sk_X509_num(untrusted); i > 0 ; i--) { X509 *this_cert = sk_X509_value(untrusted, i - 1); @@ -3417,7 +3434,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx) } } /* check_cert_cn */ -#ifdef HAVE_OPENSSL_OCSP_H +#if OPENSSL_VERSION_NUMBER >= 0x10100000L && defined(HAVE_OPENSSL_OCSP_H) if (my_ok) { /* * No OCSP, allow external verification. @@ -3447,7 +3464,7 @@ int cbtls_verify(int ok, X509_STORE_CTX *ctx) * run the external verification routine. If it's marked as * "skip verify on OK", then we don't do verify. */ - my_ok = ocsp_check(request, ocsp_store, issuer_cert, client_cert, conf); + my_ok = ocsp_check(request, ocsp_store, issuer_cert, client_cert, untrusted, conf); if (my_ok != OCSP_STATUS_FAILED) { do_verify = !conf->verify_skip_if_ocsp_ok; } @@ -4161,6 +4178,16 @@ post_ca: ERROR("Unknown or unsupported value for tls_min_version '%s'", conf->tls_min_version); return NULL; } + +#ifdef WITH_RADIUSV11 + /* + * RADIUS 1.1 requires TLS 1.3 or later. + */ + if (conf->radiusv11 && (min_version < TLS1_3_VERSION)) { + WARN(LOG_PREFIX ": The configuration allows TLS <1.3. RADIUS/1.1 MUST use TLS 1.3"); + WARN(LOG_PREFIX ": Please set: tls_min_version = '1.3'"); + } +#endif } else { #ifdef WITH_RADIUSV11 /* diff --git a/src/main/tls_listen.c b/src/main/tls_listen.c index fa8c382..3dc786b 100644 --- a/src/main/tls_listen.c +++ b/src/main/tls_listen.c @@ -377,7 +377,6 @@ static int tls_socket_recv(rad_listen_t *listener) REQUEST *request; listen_socket_t *sock = listener->data; fr_tls_status_t status; - RADCLIENT *client = sock->client; if (!sock->packet) { sock->packet = rad_alloc(sock, false); @@ -580,6 +579,7 @@ check_for_setup: * or any other contents. */ request->packet->code = PW_CODE_STATUS_SERVER; + request->packet->id = request->reply->id = 0; request->packet->data = talloc_zero_array(request->packet, uint8_t, 20); request->packet->data[0] = PW_CODE_STATUS_SERVER; request->packet->data[3] = 20; @@ -673,6 +673,7 @@ read_application_data: #ifdef WITH_RADIUSV11 packet->radiusv11 = sock->radiusv11; #endif + packet->tls = true; if (!rad_packet_ok(packet, 0, NULL)) { if (DEBUG_ENABLED) ERROR("Receive - %s", fr_strerror()); @@ -708,8 +709,6 @@ read_application_data: } } - FR_STATS_INC(auth, total_requests); - return 1; } @@ -874,6 +873,7 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request) */ if (sock->state == LISTEN_TLS_CHECKING) { if (request->reply->code != PW_CODE_ACCESS_ACCEPT) { + RDEBUG("(TLS) Connection checks failed - closing connection"); listener->status = RAD_LISTEN_STATUS_EOL; listener->tls = NULL; /* parent owns this! */ @@ -887,6 +887,7 @@ int dual_tls_send(rad_listen_t *listener, REQUEST *request) /* * Resume reading from the listener. */ + RDEBUG("(TLS) Connection checks succeeded - continuing with normal reads"); listener->status = RAD_LISTEN_STATUS_RESUME; radius_update_listener(listener); @@ -1286,6 +1287,7 @@ int proxy_tls_recv(rad_listen_t *listener) } #endif + packet->tls = true; /* * FIXME: Client MIB updates? @@ -1373,6 +1375,7 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request) * if there's no packet, encode it here. */ if (!request->proxy->data) { + request->reply->tls = true; request->proxy_listener->proxy_encode(request->proxy_listener, request); } @@ -1406,9 +1409,11 @@ int proxy_tls_send(rad_listen_t *listener, REQUEST *request) return -1; } + RDEBUG3("(TLS) has %zu bytes in the buffer", sock->ssn->clean_out.used); + memcpy(sock->ssn->clean_out.data + sock->ssn->clean_out.used, request->proxy->data, request->proxy->data_len); sock->ssn->clean_out.used += request->proxy->data_len; - RDEBUG3("(TLS) Writing %zu bytes for later (total %zu)", request->proxy->data_len, sock->ssn->clean_out.used); + RDEBUG3("(TLS) Saving %zu bytes of RADIUS traffic for later (total %zu)", request->proxy->data_len, sock->ssn->clean_out.used); PTHREAD_MUTEX_UNLOCK(&sock->mutex); return 0; @@ -1508,6 +1513,8 @@ int proxy_tls_send_reply(rad_listen_t *listener, REQUEST *request) if ((listener->status != RAD_LISTEN_STATUS_INIT && (listener->status != RAD_LISTEN_STATUS_KNOWN))) return 0; + request->reply->tls = true; + /* * Pack the VPs */ diff --git a/src/main/tmpl.c b/src/main/tmpl.c index 6ec2598..6746bde 100644 --- a/src/main/tmpl.c +++ b/src/main/tmpl.c @@ -579,6 +579,7 @@ ssize_t tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name, long num; char *q; tmpl_type_t type = TMPL_TYPE_ATTR; + DICT_ATTR const *da; value_pair_tmpl_attr_t attr; /* So we don't fill the tmpl with junk and then error out */ @@ -694,6 +695,16 @@ ssize_t tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name, } /* + * Canonicalize the attribute. + * + * We can define multiple names for one attribute. In + * which case we only use the canonical name. + */ + da = dict_attrbyvalue(attr.da->attr, attr.da->vendor); + if (da && (attr.da != da)) attr.da = da; + + + /* * The string MIGHT have a tag. */ if (*p == ':') { diff --git a/src/main/unittest.c b/src/main/unittest.c index 72fdadc..c82d31d 100644 --- a/src/main/unittest.c +++ b/src/main/unittest.c @@ -55,7 +55,7 @@ char const *radiusd_version = "FreeRADIUS Version " RADIUSD_VERSION_STRING #endif ; -fr_event_list_t *el = NULL; +static fr_event_list_t *el = NULL; /* * Static functions. diff --git a/src/main/util.c b/src/main/util.c index b216cc9..607bcaa 100644 --- a/src/main/util.c +++ b/src/main/util.c @@ -398,15 +398,15 @@ size_t rad_filename_escape(UNUSED REQUEST *request, char *out, size_t outlen, ch switch (utf8_len) { case 2: - snprintf(out, freespace, "-%x-%x", in[0], in[1]); + snprintf(out, freespace, "-%x-%x", (uint8_t)in[0], (uint8_t)in[1]); break; case 3: - snprintf(out, freespace, "-%x-%x-%x", in[0], in[1], in[2]); + snprintf(out, freespace, "-%x-%x-%x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2]); break; case 4: - snprintf(out, freespace, "-%x-%x-%x-%x", in[0], in[1], in[2], in[3]); + snprintf(out, freespace, "-%x-%x-%x-%x", (uint8_t)in[0], (uint8_t)in[1], (uint8_t)in[2], (uint8_t)in[3]); break; } diff --git a/src/main/version.c b/src/main/version.c index 2fe3428..c190337 100644 --- a/src/main/version.c +++ b/src/main/version.c @@ -613,7 +613,7 @@ void version_print(void) DEBUG2(" "); } INFO("FreeRADIUS Version " RADIUSD_VERSION_STRING); - INFO("Copyright (C) 1999-2022 The FreeRADIUS server project and contributors"); + INFO("Copyright (C) 1999-2023 The FreeRADIUS server project and contributors"); INFO("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A"); INFO("PARTICULAR PURPOSE"); INFO("You may redistribute copies of FreeRADIUS under the terms of the"); diff --git a/src/modules/proto_dhcp/dhcpclient.c b/src/modules/proto_dhcp/dhcpclient.c index 5ab4365..7feabbf 100644 --- a/src/modules/proto_dhcp/dhcpclient.c +++ b/src/modules/proto_dhcp/dhcpclient.c @@ -52,9 +52,10 @@ struct sockaddr_ll ll; /* Socket address structure */ static char *iface = NULL; static int iface_ind = -1; -# define DEBUG if (fr_debug_lvl && fr_log_fp) fr_printf_log #endif +# define DEBUG if (fr_debug_lvl && fr_log_fp) fr_printf_log + static RADIUS_PACKET *reply = NULL; static bool reply_expected = true; @@ -101,6 +102,7 @@ static void NEVER_RETURNS usage(void) #ifdef HAVE_LINUX_IF_PACKET_H fprintf(stderr, " -i <interface> Use this interface to send/receive at packet level on a raw socket.\n"); #endif + fprintf(stderr, " -r <retries> On timeout, retry sending the packet 'retries' times.\n"); fprintf(stderr, " -t <timeout> Wait 'timeout' seconds for a reply (may be a floating point number).\n"); fprintf(stderr, " -v Show program version information.\n"); fprintf(stderr, " -x Debugging mode.\n"); @@ -130,6 +132,7 @@ static RADIUS_PACKET *request_init(char const *filename) return NULL; } } else { + DEBUG("Reading packets from stdin\n"); fp = stdin; } diff --git a/src/modules/rlm_counter/rlm_counter.c b/src/modules/rlm_counter/rlm_counter.c index ff46aef..9e3c753 100644 --- a/src/modules/rlm_counter/rlm_counter.c +++ b/src/modules/rlm_counter/rlm_counter.c @@ -839,7 +839,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque /* * User is denied access, send back a reply message */ - sprintf(msg, "Your maximum %s usage time has been reached", inst->reset); + snprintf(msg, sizeof(msg), "Your maximum %s usage time has been reached", inst->reset); pair_make_reply("Reply-Message", msg, T_OP_EQ); REDEBUG("Maximum %s usage time reached", inst->reset); diff --git a/src/modules/rlm_date/rlm_date.c b/src/modules/rlm_date/rlm_date.c index 79191e7..739395c 100644 --- a/src/modules/rlm_date/rlm_date.c +++ b/src/modules/rlm_date/rlm_date.c @@ -19,9 +19,10 @@ * @brief Translates timestrings between formats. * * @author Artur Malinowski <artur@wow.com> + * @author Matthew Newton * * @copyright 2013 Artur Malinowski <artur@wow.com> - * @copyright 1999-2013 The FreeRADIUS Server Project. + * @copyright 1999-2023 The FreeRADIUS Server Project. */ #include <freeradius-devel/radiusd.h> @@ -116,6 +117,173 @@ static ssize_t xlat_date_convert(void *instance, REQUEST *request, char const *f } DIAG_ON(format-nonliteral) + +/** Get time in ms since either epoch or another value + * + * %{time_since:s} - return seconds since epoch + * %{time_since:ms 0} - return milliseconds since epoch + * %{time_since:us 1695745763034443} - return microseconds since Tue 26 Sep 17:29:23.034443 BST 2023 + * %{time_since:us &Tmp-Integer64-1} - return microseconds since value in &Tmp-Integer64-1 + */ + +static ssize_t xlat_time_since(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) +{ + uint64_t time_now = 0; + uint64_t time_delta = 0; + uint64_t time_since = 0; + struct timeval tv; + + enum timebase { + S = 1, + MS = 1000, + US = 1000000, + }; + enum timebase time_base; + + while (isspace((uint8_t) *fmt)) fmt++; + + /* + * Work out what time base we are using, s, ms or us. + */ + if (fmt[0] == 'm' && fmt[1] == 's') { + time_base = MS; + fmt += 2; + } else if (fmt[0] == 'u' && fmt[1] == 's') { + time_base = US; + fmt += 2; + } else if (fmt[0] == 's') { + time_base = S; + fmt++; + } else { + REDEBUG("Time base (ms, us, s) missing in time_since xlat"); + error: + *out = '\0'; + return -1; + } + + if (fmt[0] != '\0' && fmt[0] != ' ') { + REDEBUG("Invalid arguments passed to time_since xlat"); + goto error; + } + + while (isspace((uint8_t) *fmt)) fmt++; + + /* + * Handle the different formats that we can be passed + */ + if (fmt[0] == '\0') { + /* + * %{time_since:[mu]?s} - epoch + */ + time_since = 0; + + } else if (fmt[0] == '&') { + /* + * We were provided with an attribute + * + * %{time_since:[mu]?s &list:Attr-Name} + */ + value_data_t outnum; + VALUE_PAIR *vp; + vp_tmpl_t vpt; + ssize_t slen; + + fmt++; + slen = tmpl_from_attr_substr(&vpt, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false); + if (slen <= 0) { + /* Attribute name doesn't exist */ + REDEBUG("Unable to parse attribute in time_since xlat"); + goto error; + } + fmt += slen; + + if (tmpl_find_vp(&vp, request, &vpt) < 0) { + /* Attribute exists but is not in the list */ + RWDEBUG("Can't find &%.*s", (int)vpt.len, vpt.name); + goto error; + } + + if (vp->da->type == PW_TYPE_INTEGER64) { + /* + * Int64 is easy + */ + time_since = vp->vp_integer64; + } else { + /* + * ...but not others - try and convert, but warn it's likely nonsensical. + */ + if (value_data_cast(request, &outnum, + PW_TYPE_INTEGER64, NULL, vp->da->type, NULL, + &vp->data, vp->vp_length) < 0) { + REDEBUG("Unable to convert %s to integer", fmt); + goto error; + } + if (vp->da->type == PW_TYPE_DATE) { + /* + * Special case a Date - we know it's seconds + */ + RDEBUG3("Attribute \"%s\" is a date; multiplying seconds by %d", fmt, time_base); + time_since = outnum.integer64 * time_base; + } else { + RWDEBUG("Attribute \"%s\" is not integer, conversion may not make sense", fmt); + time_since = outnum.integer64; + } + } + + } else if (fmt[0] == '-') { + REDEBUG("time_since xlat only accepts positive integers"); + goto error; + + } else { + /* + * Otherwise we hope we were provided with an integer value + * + * %{time_since:[mu]?s 12345} + */ + if (sscanf(fmt, "%" PRIu64, &time_since) != 1) { + REDEBUG("Failed parsing \"%s\" as integer", fmt); + goto error; + } + } + + /* + * Get current time and add milli/micro component if needed + */ + gettimeofday(&tv, NULL); + + time_now = (uint64_t)tv.tv_sec * time_base; + + if (time_base == MS) { + time_now += (uint64_t)tv.tv_usec / 1000; + } else if (time_base == US) { + time_now += (uint64_t)tv.tv_usec; + } + + /* + * time_since needs to be in the past + */ + if (time_since > time_now) { + REDEBUG("time provided to time_since needs to be in the past"); + goto error; + } + + /* + * Calculate time since provided value + */ + time_delta = time_now - time_since; + + /* + * Write out and return + */ + if ((size_t)snprintf(out, outlen, "%" PRIu64, time_delta) >= outlen) { + REDEBUG("Insufficient space to write 64-bit time value"); + goto error; + } + + return 0; +} + + static int mod_bootstrap(CONF_SECTION *conf, void *instance) { rlm_date_t *inst = instance; @@ -126,6 +294,7 @@ static int mod_bootstrap(CONF_SECTION *conf, void *instance) } xlat_register(inst->xlat_name, xlat_date_convert, NULL, inst); + xlat_register("time_since", xlat_time_since, NULL, inst); return 0; } diff --git a/src/modules/rlm_detail/rlm_detail.c b/src/modules/rlm_detail/rlm_detail.c index 036549f..949811c 100644 --- a/src/modules/rlm_detail/rlm_detail.c +++ b/src/modules/rlm_detail/rlm_detail.c @@ -64,6 +64,8 @@ typedef struct detail_instance { bool escape; //!< do filename escaping, yes / no + bool dates_as_integer; + xlat_escape_t escape_func; //!< escape function exfile_t *ef; //!< Log file handler @@ -79,6 +81,7 @@ static const CONF_PARSER module_config[] = { { "permissions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_detail_t, perm), "0600" }, { "group", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_detail_t, group), NULL }, { "locking", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_detail_t, locking), "no" }, + { "dates_as_integer", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_detail_t, dates_as_integer), "no" }, { "escape_filenames", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_detail_t, escape), "no" }, { "log_packet_header", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_detail_t, log_srcdst), "no" }, CONF_PARSER_TERMINATOR @@ -317,7 +320,12 @@ static int detail_write(FILE *out, rlm_detail_t *inst, REQUEST *request, RADIUS_ */ op = vp->op; vp->op = T_OP_EQ; - vp_print(out, vp); + + if ((vp->da->type == PW_TYPE_DATE) && inst->dates_as_integer) { + WRITE("\t%s = %u\n", vp->da->name, vp->vp_date); + } else { + vp_print(out, vp); + } vp->op = op; } } @@ -336,7 +344,7 @@ static int detail_write(FILE *out, rlm_detail_t *inst, REQUEST *request, RADIUS_ } #endif } - WRITE("\tTimestamp = %ld\n", (unsigned long) request->timestamp); + WRITE("\tTimestamp = %lu\n", (unsigned long) request->timestamp); WRITE("\n"); diff --git a/src/modules/rlm_dpsk/all.mk b/src/modules/rlm_dpsk/all.mk new file mode 100644 index 0000000..8da2475 --- /dev/null +++ b/src/modules/rlm_dpsk/all.mk @@ -0,0 +1,10 @@ +TARGETNAME := rlm_dpsk + +ifneq "$(OPENSSL_LIBS)" "" +TARGET := $(TARGETNAME).a +endif + +SOURCES := $(TARGETNAME).c + +SRC_CFLAGS := +TGT_LDLIBS := diff --git a/src/modules/rlm_dpsk/rlm_dpsk.c b/src/modules/rlm_dpsk/rlm_dpsk.c new file mode 100644 index 0000000..6ca43ee --- /dev/null +++ b/src/modules/rlm_dpsk/rlm_dpsk.c @@ -0,0 +1,940 @@ +/* + * Copyright (C) 2023 Network RADIUS SARL (legal@networkradius.com) + * + * This software may not be redistributed in any form without the prior + * written consent of Network RADIUS. + * + * 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. + */ + +/** + * $Id$ + * @file rlm_dpsk.c + * @brief Dynamic PSK for WiFi + * + * @copyright 2023 Network RADIUS SAS (legal@networkradius.com) + */ +RCSID("$Id$") + +#include <freeradius-devel/radiusd.h> +#include <freeradius-devel/modules.h> +#include <freeradius-devel/dlist.h> +#include <freeradius-devel/rad_assert.h> + +#include <openssl/ssl.h> +#include <openssl/evp.h> +#include <openssl/hmac.h> + +#include <ctype.h> + +#define PW_FREERADIUS_8021X_ANONCE (1) +#define PW_FREERADIUS_8021X_EAPOL_KEY_MSG (2) + +#define VENDORPEC_FREERADIUS_EVS5 ((((uint32_t) 245) << 24) | VENDORPEC_FREERADIUS) + +#define VENDORPEC_RUCKUS (25053) +#define PW_RUCKUS_BSSID (14) +#define PW_RUCKUS_DPSK_PARAMS (152) + +//#define PW_RUCKUS_DPSK_CIPHER (PW_RUCKUS_DPSK_PARAMS | (2 << 8)) +#define PW_RUCKUS_DPSK_ANONCE (PW_RUCKUS_DPSK_PARAMS | (3 << 8)) +#define PW_RUCKUS_DPSK_EAPOL_KEY_FRAME (PW_RUCKUS_DPSK_PARAMS | (4 << 8)) + + +/* + Header: 02030075 + + descriptor 02 + information 010a + length 0010 + replay counter 000000000000001 + snonce c3bb319516614aacfb44e933bf1671131fb1856e5b2721952d414ce3f5aa312b + IV 0000000000000000000000000000000 + rsc 0000000000000000 + reserved 0000000000000000 + mic 35cddcedad0dfb6a12a2eca55c17c323 + data length 0016 + data 30140100000fac040100000fac040100000fac028c00 + + 30 + 14 length of data + 01 ... +*/ + +typedef struct eapol_key_frame_t { + uint8_t descriptor; // message number 2 + uint16_t information; // + uint16_t length; // always 0010, for 16 octers + uint8_t replay_counter[8]; // usually "1" + uint8_t nonce[32]; // random token + uint8_t iv[16]; // zeroes + uint8_t rsc[8]; // zeros + uint8_t reserved[8]; // zeroes + uint8_t mic[16]; // calculated data + uint16_t data_len; // various other things we don't need. +// uint8_t data[]; +} CC_HINT(__packed__) eapol_key_frame_t; + +typedef struct eapol_attr_t { + uint8_t header[4]; // 02030075 + eapol_key_frame_t frame; +} CC_HINT(__packed__) eapol_attr_t; + +#ifdef HAVE_PTHREAD_H +#define PTHREAD_MUTEX_LOCK pthread_mutex_lock +#define PTHREAD_MUTEX_UNLOCK pthread_mutex_unlock +#else +#define PTHREAD_MUTEX_LOCK(_x) +#define PTHREAD_MUTEX_UNLOCK(_x) +#endif + +typedef struct rlm_dpsk_s rlm_dpsk_t; + +typedef struct { + uint8_t mac[6]; + uint8_t pmk[32]; + + uint8_t *ssid; + size_t ssid_len; + + char *identity; + size_t identity_len; + + uint8_t *psk; + size_t psk_len; + time_t expires; + + fr_dlist_t dlist; + rlm_dpsk_t *inst; +} rlm_dpsk_cache_t; + +struct rlm_dpsk_s { + char const *xlat_name; + bool ruckus; + + rbtree_t *cache; + + uint32_t cache_size; + uint32_t cache_lifetime; + + char const *filename; + +#ifdef HAVE_PTHREAD_H + pthread_mutex_t mutex; +#endif + fr_dlist_t head; + + DICT_ATTR const *ssid; + DICT_ATTR const *anonce; + DICT_ATTR const *frame; +}; + +static const CONF_PARSER module_config[] = { + { "ruckus", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_dpsk_t, ruckus), "no" }, + + { "cache_size", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_dpsk_t, cache_size), "0" }, + { "cache_lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_dpsk_t, cache_lifetime), "0" }, + + { "filename", FR_CONF_OFFSET(PW_TYPE_FILE_INPUT, rlm_dpsk_t, filename), NULL }, + + CONF_PARSER_TERMINATOR +}; + + +static inline CC_HINT(nonnull) rlm_dpsk_cache_t *fr_dlist_head(fr_dlist_t const *head) +{ + if (head->prev == head) return NULL; + + return (rlm_dpsk_cache_t *) (((uintptr_t) head->next) - offsetof(rlm_dpsk_cache_t, dlist)); +} + +static void rdebug_hex(REQUEST *request, char const *prefix, uint8_t const *data, int len) +{ + int i; + char buffer[2048]; /* large enough for largest len */ + + /* + * Leave a trailing space, we don't really care about that. + */ + for (i = 0; i < len; i++) { + snprintf(buffer + i * 2, sizeof(buffer) - i * 2, "%02x", data[i]); + } + + RDEBUG("%s %s", prefix, buffer); +} +#define RDEBUG_HEX if (rad_debug_lvl >= 3) rdebug_hex + +#if 0 +/* + * Find the Ruckus attributes, and convert to FreeRADIUS ones. + * + * Also check the WPA2 cipher. We need AES + HMAC-SHA1. + */ +static bool normalize(rlm_dpsk_t *inst, REQUEST *request) +{ + VALUE_PAIR *bssid, *cipher, *anonce, *key_msg, *vp; + + if (!inst->ruckus) return false; + + bssid = fr_pair_find_by_num(request->packet->vps, PW_RUCKUS_BSSID, VENDORPEC_RUCKUS, TAG_ANY); + if (!bssid) return false; + + cipher = fr_pair_find_by_num(request->packet->vps, PW_RUCKUS_DPSK_CIPHER, VENDORPEC_RUCKUS, TAG_ANY); + if (!cipher) return false; + + if (cipher->vp_byte != 4) { + RDEBUG("Found Ruckus-DPSK-Cipher != 4, which means that we cannot do DPSK"); + return false; + } + + anonce = fr_pair_find_by_num(request->packet->vps, PW_RUCKUS_DPSK_ANONCE, VENDORPEC_RUCKUS, TAG_ANY); + if (!anonce) return false; + + key_msg = fr_pair_find_by_num(request->packet->vps, PW_RUCKUS_DPSK_EAPOL_KEY_FRAME, VENDORPEC_RUCKUS, TAG_ANY); + if (!key_msg) return false; + + MEM(vp = fr_pair_afrom_da(request->packet, anonce->da)); + fr_pair_value_memcpy(vp, anonce->vp_octets, anonce->vp_length); + fr_pair_add(&request->packet->vps, vp); + + MEM(vp = fr_pair_afrom_da(request->packet, key_msg->da)); + fr_pair_value_memcpy(vp, key_msg->vp_octets, key_msg->vp_length); + fr_pair_add(&request->packet->vps, vp); + + return false; +} +#endif + +/* + * mod_authorize() - authorize user if we can authenticate + * it later. Add Auth-Type attribute if present in module + * configuration (usually Auth-Type must be "DPSK") + */ +static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void * instance, REQUEST *request) +{ + rlm_dpsk_t *inst = instance; + + if (!fr_pair_find_by_da(request->packet->vps, inst->anonce, TAG_ANY) && + !fr_pair_find_by_da(request->packet->vps, inst->frame, TAG_ANY)) { + return RLM_MODULE_NOOP; + } + + if (fr_pair_find_by_num(request->config, PW_AUTH_TYPE, 0, TAG_ANY)) { + RWDEBUG2("Auth-Type already set. Not setting to %s", inst->xlat_name); + return RLM_MODULE_NOOP; + } + + RDEBUG2("Found %s. Setting 'Auth-Type = %s'", inst->frame->name, inst->xlat_name); + + /* + * Set Auth-Type to MS-CHAP. The authentication code + * will take care of turning cleartext passwords into + * NT/LM passwords. + */ + if (!pair_make_config("Auth-Type", inst->xlat_name, T_OP_EQ)) { + return RLM_MODULE_FAIL; + } + + return RLM_MODULE_OK; +} + +static rlm_dpsk_cache_t *dpsk_cache_find(REQUEST *request, rlm_dpsk_t const *inst, uint8_t *buffer, size_t buflen, VALUE_PAIR *ssid, uint8_t const *mac) +{ + rlm_dpsk_cache_t *entry, my_entry; + + memcpy(my_entry.mac, mac, sizeof(my_entry.mac)); + memcpy(&my_entry.ssid, &ssid->vp_octets, sizeof(my_entry.ssid)); /* const issues */ + my_entry.ssid_len = ssid->vp_length; + + entry = rbtree_finddata(inst->cache, &my_entry); + if (entry) { + if (entry->expires > request->timestamp) { + RDEBUG3("Cache entry found"); + memcpy(buffer, entry->pmk, buflen); + return entry; + } + + RDEBUG3("Cache entry has expired"); + rbtree_deletebydata(inst->cache, entry); + } + + return NULL; +} + + +static int generate_pmk(REQUEST *request, rlm_dpsk_t const *inst, uint8_t *buffer, size_t buflen, VALUE_PAIR *ssid, uint8_t const *mac, char const *psk, size_t psk_len) +{ + VALUE_PAIR *vp; + + fr_assert(buflen == 32); + + if (!ssid) { + ssid = fr_pair_find_by_da(request->packet->vps, inst->ssid, TAG_ANY); + if (!ssid) { + RDEBUG("No %s in the request", inst->ssid->name); + return 0; + } + } + + /* + * No provided PSK. Try to look it up in the cache. If + * it isn't there, find it in the config items. + */ + if (!psk) { + if (inst->cache && mac) { + rlm_dpsk_cache_t *entry; + + entry = dpsk_cache_find(request, inst, buffer, buflen, ssid, mac); + if (entry) { + memcpy(buffer, entry->pmk, buflen); + return 1; + } + RDEBUG3("Cache entry not found"); + } /* else no caching */ + + vp = fr_pair_find_by_num(request->config, PW_PRE_SHARED_KEY, 0, TAG_ANY); + if (!vp) { + RDEBUG("No &config:Pre-Shared-Key"); + return 0; + } + + psk = vp->vp_strvalue; + psk_len = vp->vp_length; + } + + if (PKCS5_PBKDF2_HMAC_SHA1((const char *) psk, psk_len, (const unsigned char *) ssid->vp_strvalue, ssid->vp_length, 4096, buflen, buffer) == 0) { + RDEBUG("Failed calling OpenSSL to calculate the PMK"); + return 0; + } + + return 1; +} + +/* + * Verify the DPSK information. + */ +static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request) +{ + rlm_dpsk_t *inst = instance; + VALUE_PAIR *anonce, *key_msg, *ssid, *vp; + rlm_dpsk_cache_t *entry; + int lineno = 0; + size_t len, psk_len; + unsigned int digest_len, mic_len; + eapol_attr_t const *eapol; + eapol_attr_t *zeroed; + FILE *fp = NULL; + char const *psk_identity = NULL, *psk; + uint8_t *p; + uint8_t const *snonce, *ap_mac; + uint8_t const *min_mac, *max_mac; + uint8_t const *min_nonce, *max_nonce; + uint8_t pmk[32]; + uint8_t s_mac[6], message[sizeof("Pairwise key expansion") + 6 + 6 + 32 + 32 + 1], frame[128]; + uint8_t digest[EVP_MAX_MD_SIZE], mic[EVP_MAX_MD_SIZE]; + char token_identity[256]; + + /* + * Search for the information in a bunch of attributes. + */ + anonce = fr_pair_find_by_da(request->packet->vps, inst->anonce, TAG_ANY); + if (!anonce) { + RDEBUG("No FreeRADIUS-802.1X-Anonce in the request"); + return RLM_MODULE_NOOP; + } + + if (anonce->vp_length != 32) { + RDEBUG("%s has incorrect length (%zu, not 32)", inst->anonce->name, anonce->vp_length); + return RLM_MODULE_NOOP; + } + + key_msg = fr_pair_find_by_da(request->packet->vps, inst->frame, TAG_ANY); + if (!key_msg) { + RDEBUG("No %s in the request", inst->frame->name); + return RLM_MODULE_NOOP; + } + + if (key_msg->vp_length < sizeof(*eapol)) { + RDEBUG("%s has incorrect length (%zu < %zu)", inst->frame->name, key_msg->vp_length, sizeof(*eapol)); + return RLM_MODULE_NOOP; + } + + if (key_msg->vp_length > sizeof(frame)) { + RDEBUG("%s has incorrect length (%zu > %zu)", inst->frame->name, key_msg->vp_length, sizeof(frame)); + return RLM_MODULE_NOOP; + } + + ssid = fr_pair_find_by_da(request->packet->vps, inst->ssid, TAG_ANY); + if (!ssid) { + RDEBUG("No %s in the request", inst->ssid->name); + return 0; + } + + /* + * Get supplicant MAC address. + */ + vp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); + if (!vp) { + RDEBUG("No &User-Name"); + return RLM_MODULE_NOOP; + } + + len = fr_hex2bin(s_mac, sizeof(s_mac), vp->vp_strvalue, vp->vp_length); + if (len != 6) { + RDEBUG("&User-Name is not a recognizable hex MAC address"); + return RLM_MODULE_NOOP; + } + + /* + * In case we're not reading from a file. + */ + vp = fr_pair_find_by_num(request->config, PW_PSK_IDENTITY, 0, TAG_ANY); + if (vp) psk_identity = vp->vp_strvalue; + + vp = fr_pair_find_by_num(request->config, PW_PRE_SHARED_KEY, 0, TAG_ANY); + if (vp) { + psk = vp->vp_strvalue; + psk_len = vp->vp_length; + } else { + psk = NULL; + psk_len = 0; + } + + /* + * Get the AP MAC address. + */ + vp = fr_pair_find_by_num(request->packet->vps, PW_CALLED_STATION_MAC, 0, TAG_ANY); + if (!vp) { + RDEBUG("No &Called-Station-MAC"); + return RLM_MODULE_NOOP; + } + + if (vp->length != 6) { + RDEBUG("&Called-Station-MAC is not a recognizable MAC address"); + return RLM_MODULE_NOOP; + } + + ap_mac = vp->vp_octets; + + /* + * Sort the MACs + */ + if (memcmp(s_mac, ap_mac, 6) <= 0) { + min_mac = s_mac; + max_mac = ap_mac; + } else { + min_mac = ap_mac; + max_mac = s_mac; + } + + eapol = (eapol_attr_t const *) key_msg->vp_octets; + + /* + * Get supplicant nonce and AP nonce. + * + * Then sort the nonces. + */ + snonce = key_msg->vp_octets + 17; + if (memcmp(snonce, anonce->vp_octets, 32) <= 0) { + min_nonce = snonce; + max_nonce = anonce->vp_octets; + } else { + min_nonce = anonce->vp_octets; + max_nonce = snonce; + } + + /* + * Create the base message which we will hash. + */ + memcpy(message, "Pairwise key expansion", sizeof("Pairwise key expansion")); /* including trailing NUL */ + p = &message[sizeof("Pairwise key expansion")]; + + memcpy(p, min_mac, 6); + memcpy(p + 6, max_mac, 6); + p += 12; + + memcpy(p, min_nonce, 32); + memcpy(p + 32, max_nonce, 32); + p += 64; + *p = '\0'; + fr_assert(sizeof(message) == (p + 1 - message)); + + if (inst->filename && !psk) { + FR_TOKEN token; + char const *q; + char token_psk[256]; + char token_mac[256]; + char buffer[1024]; + + /* + * If there's a cached entry, we don't read the file. + */ + entry = dpsk_cache_find(request, inst, pmk, sizeof(pmk), ssid, s_mac); + if (entry) { + psk_identity = entry->identity; + goto make_digest; + } + + RDEBUG3("Looking for PSK in file %s", inst->filename); + + fp = fopen(inst->filename, "r"); + if (!fp) { + REDEBUG("Failed opening %s - %s", inst->filename, fr_syserror(errno)); + return RLM_MODULE_FAIL; + } + +get_next_psk: + q = fgets(buffer, sizeof(buffer), fp); + if (!q) { + RDEBUG("Failed to find matching key in %s", inst->filename); + fail: + fclose(fp); + return RLM_MODULE_FAIL; + } + + /* + * Split the line on commas, paying attention to double quotes. + */ + token = getstring(&q, token_identity, sizeof(token_identity), true); + if (token == T_INVALID) { + RDEBUG("%s[%d] Failed parsing identity", inst->filename, lineno); + goto fail; + } + + if (*q != ',') { + RDEBUG("%s[%d] Failed to find ',' after identity", inst->filename, lineno); + goto fail; + } + q++; + + token = getstring(&q, token_psk, sizeof(token_psk), true); + if (token == T_INVALID) { + RDEBUG("%s[%d] Failed parsing PSK", inst->filename, lineno); + goto fail; + } + + if (*q == ',') { + q++; + + token = getstring(&q, token_mac, sizeof(token_mac), true); + if (token == T_INVALID) { + RDEBUG("%s[%d] Failed parsing MAC", inst->filename, lineno); + goto fail; + } + + /* + * See if the MAC matches. If not, skip + * this entry. That's a basic negative cache. + */ + if ((strlen(token_mac) != 12) || + (fr_hex2bin((uint8_t *) token_mac, 6, token_mac, 12) != 12)) { + RDEBUG("%s[%d] Failed parsing MAC", inst->filename, lineno); + goto fail; + } + + if (memcmp(s_mac, token_mac, 6) != 0) { + psk_identity = NULL; + goto get_next_psk; + } + + /* + * Close the file so that we don't check any other entries. + */ + MEM(vp = fr_pair_afrom_num(request, PW_PRE_SHARED_KEY, 0)); + fr_pair_value_bstrncpy(vp, token_psk, strlen(token_psk)); + + fr_pair_add(&request->config, vp); + fclose(fp); + fp = NULL; + + RDEBUG3("Found matching MAC"); + } + + /* + * Generate the PMK using the SSID, this MAC, and the PSK we just read. + */ + RDEBUG3("%s[%d] Trying PSK %s", inst->filename, lineno, token_psk); + if (generate_pmk(request, inst, pmk, sizeof(pmk), ssid, s_mac, token_psk, strlen(token_psk)) == 0) { + RDEBUG("No &config:Pairwise-Master-Key or &config:Pre-Shared-Key found"); + return RLM_MODULE_NOOP; + } + + /* + * Remember which identity we had + */ + psk_identity = token_identity; + goto make_digest; + } + + /* + * Use the PMK if it already exists. Otherwise calculate it from the PSK. + */ + vp = fr_pair_find_by_num(request->config, PW_PAIRWISE_MASTER_KEY, 0, TAG_ANY); + if (!vp) { + if (generate_pmk(request, inst, pmk, sizeof(pmk), ssid, s_mac, psk, psk_len) == 0) { + RDEBUG("No &config:Pairwise-Master-Key or &config:Pre-Shared-Key found"); + fr_assert(!fp); + return RLM_MODULE_NOOP; + } + + } else if (vp->vp_length != sizeof(pmk)) { + RDEBUG("Pairwise-Master-Key has incorrect length (%zu != %zu)", vp->vp_length, sizeof(pmk)); + fr_assert(!fp); + return RLM_MODULE_NOOP; + + } else { + memcpy(pmk, vp->vp_octets, sizeof(pmk)); + } + + /* + * HMAC = HMAC_SHA1(pmk, message); + * + * We need the first 16 octets of this. + */ +make_digest: + digest_len = sizeof(digest); + HMAC(EVP_sha1(), pmk, sizeof(pmk), message, sizeof(message), digest, &digest_len); + + RDEBUG_HEX(request, "message:", message, sizeof(message)); + RDEBUG_HEX(request, "pmk :", pmk, sizeof(pmk)); + RDEBUG_HEX(request, "kck :", digest, 16); + + /* + * Create the frame with the middle field zero, and hash it with the KCK digest we calculated from the key expansion. + */ + memcpy(frame, key_msg->vp_octets, key_msg->vp_length); + zeroed = (eapol_attr_t *) &frame[0]; + memset(&zeroed->frame.mic[0], 0, 16); + + RDEBUG_HEX(request, "zeroed:", frame, key_msg->vp_length); + + mic_len = sizeof(mic); + HMAC(EVP_sha1(), digest, 16, frame, key_msg->vp_length, mic, &mic_len); + + /* + * Do the MICs match? + */ + if (memcmp(&eapol->frame.mic[0], mic, 16) != 0) { + if (fp) { + psk_identity = NULL; + goto get_next_psk; + } + + RDEBUG_HEX(request, "calculated mic:", mic, 16); + RDEBUG_HEX(request, "packet mic :", &eapol->frame.mic[0], 16); + return RLM_MODULE_FAIL; + } + + /* + * It matches. Close the input file if necessary. + */ + if (fp) fclose(fp); + + /* + * Extend the lifetime of the cache entry, or add the + * cache entry if necessary. + */ + if (inst->cache) { + rlm_dpsk_cache_t my_entry; + + /* + * Find the entry (again), and update the expiry time. + * + * Create the entry if neessary. + */ + memcpy(my_entry.mac, s_mac, sizeof(my_entry.mac)); + + vp = fr_pair_find_by_da(request->packet->vps, inst->ssid, TAG_ANY); + if (!vp) goto save_psk; /* should never really happen, but just to be safe */ + + memcpy(&my_entry.ssid, &vp->vp_octets, sizeof(my_entry.ssid)); /* const issues */ + my_entry.ssid_len = vp->vp_length; + + entry = rbtree_finddata(inst->cache, &my_entry); + if (!entry) { + /* + * Too many entries in the cache. Delete the oldest one. + */ + if (rbtree_num_elements(inst->cache) > inst->cache_size) { + PTHREAD_MUTEX_LOCK(&inst->mutex); + entry = fr_dlist_head(&inst->head); + PTHREAD_MUTEX_UNLOCK(&inst->mutex); + + rbtree_deletebydata(inst->cache, entry); + } + + MEM(entry = talloc_zero(NULL, rlm_dpsk_cache_t)); + + memcpy(entry->mac, s_mac, sizeof(entry->mac)); + memcpy(entry->pmk, pmk, sizeof(entry->pmk)); + + fr_dlist_entry_init(&entry->dlist); + entry->inst = inst; + + /* + * Save the variable-length SSID. + */ + MEM(entry->ssid = talloc_memdup(entry, vp->vp_octets, vp->vp_length)); + entry->ssid_len = vp->vp_length; + + /* + * Save the PSK. If we just have the + * PMK, then we can still cache that. + */ + vp = fr_pair_find_by_num(request->config, PW_PRE_SHARED_KEY, 0, TAG_ANY); + if (vp) { + MEM(entry->psk = talloc_memdup(entry, vp->vp_octets, vp->vp_length)); + entry->psk_len = vp->vp_length; + } + + /* + * Save the identity. + */ + if (psk_identity) { + MEM(entry->identity = talloc_memdup(entry, psk_identity, strlen(psk_identity))); + entry->identity_len = strlen(psk_identity); + } + + /* + * Cache it. + */ + if (!rbtree_insert(inst->cache, entry)) { + talloc_free(entry); + goto save_found_psk; + } + RDEBUG3("Cache entry saved"); + } + entry->expires = request->timestamp + inst->cache_lifetime; + + PTHREAD_MUTEX_LOCK(&inst->mutex); + fr_dlist_entry_unlink(&entry->dlist); + fr_dlist_insert_tail(&inst->head, &entry->dlist); + PTHREAD_MUTEX_UNLOCK(&inst->mutex); + + /* + * Add the PSK to the reply items, if it was cached. + */ + if (entry->psk) { + MEM(vp = fr_pair_afrom_num(request->reply, PW_PRE_SHARED_KEY, 0)); + fr_pair_value_bstrncpy(vp, entry->psk, entry->psk_len); + + fr_pair_add(&request->reply->vps, vp); + } + + goto save_psk_identity; + } + + /* + * Save a copy of the found PSK in the reply; + */ +save_psk: + vp = fr_pair_find_by_num(request->config, PW_PRE_SHARED_KEY, 0, TAG_ANY); + +save_found_psk: + if (!vp) return RLM_MODULE_OK; + + fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp)); + +save_psk_identity: + /* + * Save which identity matched. + */ + if (psk_identity) { + MEM(vp = fr_pair_afrom_num(request->reply, PW_PSK_IDENTITY, 0)); + fr_pair_value_bstrncpy(vp, psk_identity, strlen(psk_identity)); + + fr_pair_add(&request->reply->vps, vp); + } + + return RLM_MODULE_OK; +} + +/* + * Generate the PMK from SSID and Pre-Shared-Key + */ +static ssize_t dpsk_xlat(void *instance, REQUEST *request, + char const *fmt, char *out, size_t outlen) +{ + rlm_dpsk_t *inst = instance; + char const *p, *ssid, *psk; + size_t ssid_len, psk_len; + uint8_t buffer[32]; + + /* + * Prefer xlat arguments. But if they don't exist, use the attributes. + */ + p = fmt; + while (isspace((uint8_t) *p)) p++; + + if (!*p) { + if (generate_pmk(request, inst, buffer, sizeof(buffer), NULL, NULL, NULL, 0) == 0) { + RDEBUG("No &request:Called-Station-SSID or &config:Pre-Shared-Key found"); + return 0; + } + } else { + ssid = p; + + while (*p && !isspace((uint8_t) *p)) p++; + + ssid_len = p - ssid; + + if (!*p) { + REDEBUG("Found SSID, but no PSK"); + return 0; + } + + psk = p; + + while (*p && !isspace((uint8_t) *p)) p++; + + psk_len = p - psk; + + if (PKCS5_PBKDF2_HMAC_SHA1(psk, psk_len, (const unsigned char *) ssid, ssid_len, 4096, sizeof(buffer), buffer) == 0) { + RDEBUG("Failed calling OpenSSL to calculate the PMK"); + return 0; + } + } + + if (outlen < sizeof(buffer) * 2 + 1) { + REDEBUG("Output buffer is too small for PMK"); + return 0; + } + + return fr_bin2hex(out, buffer, 32); +} + +static int mod_bootstrap(CONF_SECTION *conf, void *instance) +{ + char const *name; + rlm_dpsk_t *inst = instance; + + /* + * Create the dynamic translation. + */ + name = cf_section_name2(conf); + if (!name) name = cf_section_name1(conf); + inst->xlat_name = name; + xlat_register(inst->xlat_name, dpsk_xlat, NULL, inst); + + if (inst->ruckus) { + inst->ssid = dict_attrbyvalue(PW_RUCKUS_BSSID, VENDORPEC_RUCKUS); + inst->anonce = dict_attrbyvalue(PW_RUCKUS_DPSK_ANONCE, VENDORPEC_RUCKUS); + inst->frame = dict_attrbyvalue(PW_RUCKUS_DPSK_EAPOL_KEY_FRAME, VENDORPEC_RUCKUS); + } else { + inst->ssid = dict_attrbyvalue(PW_CALLED_STATION_SSID, 0); + inst->anonce = dict_attrbyvalue(PW_FREERADIUS_8021X_ANONCE, VENDORPEC_FREERADIUS_EVS5); + inst->frame = dict_attrbyvalue(PW_FREERADIUS_8021X_EAPOL_KEY_MSG, VENDORPEC_FREERADIUS_EVS5); + } + + if (!inst->ssid || !inst->anonce || !inst->frame) { + cf_log_err_cs(conf, "Failed to find attributes in the dictionary. Please do not edit the default dictionaries!"); + return -1; + } + + return 0; +} + +static int cmp_cache_entry(void const *one, void const *two) +{ + rlm_dpsk_cache_t const *a = (rlm_dpsk_cache_t const *) one; + rlm_dpsk_cache_t const *b = (rlm_dpsk_cache_t const *) two; + int rcode; + + rcode = memcmp(a->mac, b->mac, sizeof(a->mac)); + if (rcode != 0) return rcode; + + if (a->ssid_len < b->ssid_len) return -1; + if (a->ssid_len > b->ssid_len) return +1; + + return memcmp(a->ssid, b->ssid, a->ssid_len); +} + +static void free_cache_entry(void *data) +{ + rlm_dpsk_cache_t *entry = (rlm_dpsk_cache_t *) data; + + PTHREAD_MUTEX_LOCK(&entry->inst->mutex); + fr_dlist_entry_unlink(&entry->dlist); + PTHREAD_MUTEX_UNLOCK(&entry->inst->mutex); + + talloc_free(entry); +} + +static int mod_instantiate(CONF_SECTION *conf, void *instance) +{ + rlm_dpsk_t *inst = instance; + + if (!inst->cache_size) return 0; + + FR_INTEGER_BOUND_CHECK("cache_size", inst->cache_size, <=, ((uint32_t) 1) << 16); + + if (!inst->cache_size) return 0; + + FR_INTEGER_BOUND_CHECK("cache_lifetime", inst->cache_lifetime, <=, (7 * 86400)); + FR_INTEGER_BOUND_CHECK("cache_lifetime", inst->cache_lifetime, >=, 3600); + + inst->cache = rbtree_create(inst, cmp_cache_entry, free_cache_entry, RBTREE_FLAG_LOCK); + if (!inst->cache) { + cf_log_err_cs(conf, "Failed creating internal cache"); + return -1; + } + + fr_dlist_entry_init(&inst->head); +#ifdef HAVE_PTHREAD_H + if (pthread_mutex_init(&inst->mutex, NULL) < 0) { + cf_log_err_cs(conf, "Failed creating mutex"); + return -1; + } +#endif + + return 0; +} + +#ifdef HAVE_PTHREAD_H +static int mod_detach(void *instance) +{ + rlm_dpsk_t *inst = instance; + + if (!inst->cache_size) return 0; + + pthread_mutex_destroy(&inst->mutex); + return 0; +} +#endif + +/* + * The module name should be the only globally exported symbol. + * That is, everything else should be 'static'. + * + * If the module needs to temporarily modify it's instantiation + * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE. + * The server will then take care of ensuring that the module + * is single-threaded. + */ +extern module_t rlm_dpsk; +module_t rlm_dpsk = { + .magic = RLM_MODULE_INIT, + .name = "dpsk", + .type = RLM_TYPE_THREAD_SAFE, + .inst_size = sizeof(rlm_dpsk_t), + .config = module_config, + .bootstrap = mod_bootstrap, + .instantiate = mod_instantiate, +#ifdef HAVE_PTHREAD_H + .detach = mod_detach, +#endif + .methods = { + [MOD_AUTHORIZE] = mod_authorize, + [MOD_AUTHENTICATE] = mod_authenticate, + }, +}; diff --git a/src/modules/rlm_eap/eap.c b/src/modules/rlm_eap/eap.c index 1ece323..5c0bba0 100644 --- a/src/modules/rlm_eap/eap.c +++ b/src/modules/rlm_eap/eap.c @@ -803,7 +803,7 @@ int eap_start(rlm_eap_t *inst, REQUEST *request) ((eap_msg->vp_octets[4] == 0) || (eap_msg->vp_octets[4] >= PW_EAP_MAX_TYPES) || (!inst->methods[eap_msg->vp_octets[4]]))) { - RDEBUG2("Ignoring Unknown EAP type"); + RDEBUG2("Ignoring Unknown EAP type %02x", eap_msg->vp_octets[4]); return EAP_NOOP; } @@ -833,6 +833,8 @@ int eap_start(rlm_eap_t *inst, REQUEST *request) } if ((eap_msg->vp_octets[4] == PW_EAP_TTLS) || + (eap_msg->vp_octets[4] == PW_EAP_FAST) || + (eap_msg->vp_octets[4] == PW_EAP_TEAP) || (eap_msg->vp_octets[4] == PW_EAP_PEAP)) { RDEBUG2("Continuing tunnel setup"); return EAP_OK; @@ -1214,7 +1216,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p, } } } else { /* packet was EAP identity */ - handler = eap_handler_alloc(inst); + handler = eap_handler_alloc(inst, request); if (!handler) { goto error; } @@ -1224,10 +1226,14 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_packet_p, */ handler->identity = eap_identity(request, handler, eap_packet); if (!handler->identity) { - RDEBUG("Identity Unknown, authentication failed"); - error2: - talloc_free(handler); - goto error; + if (!inst->allow_empty_identities) { + RDEBUG("Identity Unknown, authentication failed"); + error2: + talloc_free(handler); + goto error; + } + + handler->identity = ""; } vp = fr_pair_find_by_num(request->packet->vps, PW_USER_NAME, 0, TAG_ANY); diff --git a/src/modules/rlm_eap/eap.h b/src/modules/rlm_eap/eap.h index b487c08..0724463 100644 --- a/src/modules/rlm_eap/eap.h +++ b/src/modules/rlm_eap/eap.h @@ -60,7 +60,6 @@ typedef enum operation_t { PROCESS } operation_t; - /* * eap_handler_t is the interface for any EAP-Type. * Each handler contains information for one specific EAP-Type. @@ -103,7 +102,9 @@ typedef struct _eap_handler { REQUEST *request; - char *identity; //!< User name from EAP-Identity + char const *identity; //!< User name from EAP-Identity + + char const *dedup; //!< dedup key EAP_DS *prev_eapds; EAP_DS *eap_ds; diff --git a/src/modules/rlm_eap/libeap/eap_tls.c b/src/modules/rlm_eap/libeap/eap_tls.c index 2f37663..3a915bc 100644 --- a/src/modules/rlm_eap/libeap/eap_tls.c +++ b/src/modules/rlm_eap/libeap/eap_tls.c @@ -69,6 +69,9 @@ tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_ handler->tls = true; + tls_conf->name = dict_valnamebyattr(PW_EAP_TYPE, 0, handler->type); + if (!tls_conf->name) tls_conf->name = "???"; + /* * Every new session is started only from EAP-TLS-START. * Before Sending EAP-TLS-START, open a new SSL session. @@ -108,23 +111,22 @@ tls_session_t *eaptls_session(eap_handler_t *handler, fr_tls_server_conf_t *tls_ */ int eaptls_start(EAP_DS *eap_ds, int peap_flag) { - EAPTLS_PACKET reply; + EAPTLS_PACKET reply; - reply.code = FR_TLS_START; - reply.length = TLS_HEADER_LEN + 1/*flags*/; + reply.code = FR_TLS_START; + reply.length = TLS_HEADER_LEN + 1/*flags*/; - reply.flags = peap_flag; - reply.flags = SET_START(reply.flags); + reply.flags = peap_flag; + reply.flags = SET_START(reply.flags); - reply.data = NULL; - reply.dlen = 0; + reply.data = NULL; + reply.dlen = 0; - eaptls_compose(eap_ds, &reply); + eaptls_compose(eap_ds, &reply); - return 1; + return 1; } - /** Send an EAP-TLS success * * Composes an EAP-TLS-Success. This is a message with code EAP_TLS_ESTABLISHED. @@ -186,12 +188,11 @@ int eaptls_success(eap_handler_t *handler, int peap_flag) /* Should never happen */ rad_assert(0); return 0; - break; } eaptls_gen_mppe_keys(request, tls_session->ssl, tls_session->label, context, context_size); - } else if (handler->type != PW_EAP_FAST) { + } else if ((handler->type != PW_EAP_FAST) && (handler->type != PW_EAP_TEAP)) { RWDEBUG("(TLS) EAP Not adding MPPE keys because there is no PRF label"); } @@ -230,13 +231,21 @@ int eaptls_fail(eap_handler_t *handler, int peap_flag) * EAP-Request. We always embed the TLS-length in all EAP-TLS * packets that we send, for easy reference purpose. Handle * fragmentation and sending the next fragment etc. + * + * FIXME: support fragmented start due to TEAP outer tlvs */ -int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) +int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn, bool start) { EAPTLS_PACKET reply; unsigned int size; - unsigned int nlen; unsigned int lbit = 0; + unsigned int obit = 0; + VALUE_PAIR *vp; + vp_cursor_t cursor; + uint32_t nlen; + uint16_t ohdr[2]; + uint32_t olen = 0; + uint32_t tls_mtu; /* This value determines whether we set (L)ength flag for EVERY packet we send and add corresponding @@ -257,16 +266,46 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) if (ssn->length_flag) { lbit = 4; } + + /* + * This is included in the first fragment, and then never + * afterwards. + */ + if (start && ssn->outer_tlvs) { + for (vp = fr_cursor_init(&cursor, &ssn->outer_tlvs); + vp; + vp = fr_cursor_next(&cursor)) { + if (vp->da->type != PW_TYPE_OCTETS) { + DEBUG("FIXME Outer-TLV %s is of not type octets", vp->da->name); + continue; + } + obit = 4; + olen += sizeof(ohdr) + vp->vp_length; + break; + } + } + if (ssn->fragment == 0) { ssn->tls_msg_len = ssn->dirty_out.used; } - reply.code = FR_TLS_REQUEST; + reply.code = start ? FR_TLS_START : FR_TLS_REQUEST; reply.flags = ssn->peap_flag; + if (start) reply.flags = SET_START(reply.flags); + + /* + * This is only true for TEAP, so only TEAP has to check the return value of this function. + */ + if (lbit + obit + olen >= ssn->mtu) { + ERROR("fragment_size is too small for outer TLVs"); + return -1; + } + + tls_mtu = ssn->mtu - lbit - obit - olen; /* Send data, NOT more than the FRAGMENT size */ - if (ssn->dirty_out.used > ssn->mtu) { - size = ssn->mtu; + if (ssn->dirty_out.used > tls_mtu) { + size = tls_mtu; reply.flags = SET_MORE_FRAGMENTS(reply.flags); /* Length MUST be included if it is the First Fragment */ if (ssn->fragment == 0) { @@ -278,7 +317,7 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) ssn->fragment = 0; } - reply.dlen = lbit + size; + reply.dlen = lbit + obit + size + olen; reply.length = TLS_HEADER_LEN + 1/*flags*/ + reply.dlen; reply.data = talloc_array(eap_ds, uint8_t, reply.length); @@ -286,10 +325,59 @@ int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) if (lbit) { nlen = htonl(ssn->tls_msg_len); - memcpy(reply.data, &nlen, lbit); + memcpy(reply.data, &nlen, sizeof(nlen)); reply.flags = SET_LENGTH_INCLUDED(reply.flags); } - (ssn->record_minus)(&ssn->dirty_out, reply.data + lbit, size); + + if (obit) { + nlen = 0; + for (vp = fr_cursor_init(&cursor, &ssn->outer_tlvs); + vp; + vp = fr_cursor_next(&cursor)) { + if (vp->da->type != PW_TYPE_OCTETS) continue; + nlen += sizeof(ohdr) + vp->vp_length; + } + + ssn->outer_tlvs_octets = talloc_array(ssn, uint8_t, olen); + if (!ssn->outer_tlvs_octets) return 0; + + nlen = htonl(nlen); + memcpy(reply.data + lbit, &nlen, sizeof(nlen)); + reply.flags = SET_OUTER_TLV_INCLUDED(reply.flags); + } + + (ssn->record_minus)(&ssn->dirty_out, reply.data + lbit + obit, size); + + /* + * Tack on the outer TLVs after the TLS data. + */ + if (obit) { + olen = 0; + for (vp = fr_cursor_init(&cursor, &ssn->outer_tlvs); + vp; + vp = fr_cursor_next(&cursor)) { + if (vp->da->type != PW_TYPE_OCTETS) continue; + + /* FIXME duplicates eap_teap_tlv_append */ + + /* + * RFC7170, Section 4.3.1 - Outer TLVs must be marked optional + */ + ohdr[0] = htons((vp->da->attr >> fr_attr_shift[1]) & fr_attr_mask[1]); + ohdr[1] = htons(vp->vp_length); + + /* use by Crypto-Binding TLV */ + memcpy(ssn->outer_tlvs_octets + olen, ohdr, sizeof(ohdr)); + olen += sizeof(ohdr); + memcpy(ssn->outer_tlvs_octets + olen, vp->vp_octets, vp->vp_length); + olen += vp->vp_length; + + memcpy(reply.data + lbit + obit + size, ohdr, sizeof(ohdr)); + size += sizeof(ohdr); + memcpy(reply.data + lbit + obit + size, vp->vp_octets, vp->vp_length); + size += vp->vp_length; + } + } eaptls_compose(eap_ds, &reply); talloc_free(reply.data); @@ -425,6 +513,9 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler) * from a fragment acknowledgement. */ if (TLS_LENGTH_INCLUDED(eaptls_packet->flags)) { + /* + * data[0] and data[1] are always zero, vi eap_vp2packet() + */ size_t total_len = eaptls_packet->data[2] * 256 | eaptls_packet->data[3]; if (frag_len > total_len) { @@ -463,6 +554,14 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler) return FR_TLS_FIRST_FRAGMENT; } + /* + * The "O" bit is only allowed for the first fragment. + */ + if (TLS_OUTER_TLV_INCLUDED(eaptls_packet->flags)) { + REDEBUG("(TLS) EAP Peer set 'O' bit after initial fragment"); + return FR_TLS_INVALID; + } + RDEBUG2("(TLS) EAP Got additional fragment with length (%zu bytes). " "Peer says more fragments will follow", frag_len); @@ -494,6 +593,10 @@ static fr_tls_status_t eaptls_verify(eap_handler_t *handler) } /* + * eap_vp2packet() ensures that the 'O' bit is not set here. + */ + + /* * The previous packet had the M flags set, but this one doesn't, * this must be the final record fragment */ @@ -563,7 +666,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st { EAPTLS_PACKET *tlspacket; uint32_t data_len = 0; - uint32_t len = 0; + uint32_t obit = 0; uint8_t *data = NULL; if (status == FR_TLS_INVALID) return NULL; @@ -599,36 +702,8 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st tlspacket->flags = eap_ds->response->type.data[0]; /* - * A quick sanity check of the flags. If we've been told - * that there's a length, and there isn't one, then stop. + * eaptls_verify() ensures that all of the flags are correct. */ - if (TLS_LENGTH_INCLUDED(tlspacket->flags) && - (tlspacket->length < 5)) { /* flags + TLS message length */ - REDEBUG("(TLS) EAP Invalid packet received: Length bit is set," - "but packet too short to contain length field"); - talloc_free(tlspacket); - return NULL; - } - - /* - * If the final TLS packet is larger than we can handle, die - * now. - * - * Likewise, if the EAP packet says N bytes, and the TLS - * packet says there's fewer bytes, it's a problem. - */ - if (TLS_LENGTH_INCLUDED(tlspacket->flags)) { - memcpy(&data_len, &eap_ds->response->type.data[1], 4); - data_len = ntohl(data_len); - if (data_len > MAX_RECORD_SIZE) { - REDEBUG("(TLS) EAP Reassembled data will be %u bytes, " - "greater than the size that we can handle (" STRINGIFY(MAX_RECORD_SIZE) " bytes)", - data_len); - talloc_free(tlspacket); - return NULL; - } - } - switch (status) { /* * The TLS Message Length field is four octets, and @@ -640,39 +715,28 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st * length should solve the problem. */ case FR_TLS_FIRST_FRAGMENT: - case FR_TLS_LENGTH_INCLUDED: - case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH: - if (tlspacket->length < 5) { /* flags + TLS message length */ - REDEBUG("(TLS) EAP Invalid packet received: Expected length, got none"); - talloc_free(tlspacket); - return NULL; - } + obit = TLS_OUTER_TLV_INCLUDED(tlspacket->flags) << 2; /* - * Extract all the TLS fragments from the - * previous eap_ds Start appending this - * fragment to the above ds + * @todo - decode outer TLVs, too */ - memcpy(&data_len, &eap_ds->response->type.data[1], sizeof(uint32_t)); - data_len = ntohl(data_len); - data = (eap_ds->response->type.data + 5/*flags+TLS-Length*/); - len = eap_ds->response->type.length - 5/*flags+TLS-Length*/; - /* - * Hmm... this should be an error, too. - */ - if (data_len > len) { - data_len = len; - } - break; + /* FALL-THROUGH */ + + case FR_TLS_LENGTH_INCLUDED: + case FR_TLS_MORE_FRAGMENTS_WITH_LENGTH: + eap_ds->response->type.data += 4 + obit; + eap_ds->response->type.length -= 4 + obit; + + /* FALL-THROUGH */ /* * Data length is implicit, from the EAP header. */ case FR_TLS_MORE_FRAGMENTS: case FR_TLS_OK: - data_len = eap_ds->response->type.length - 1/*flags*/; - data = eap_ds->response->type.data + 1/*flags*/; + data_len = eap_ds->response->type.length - 1; + data = eap_ds->response->type.data + 1; break; default: @@ -689,6 +753,7 @@ static EAPTLS_PACKET *eaptls_extract(REQUEST *request, EAP_DS *eap_ds, fr_tls_st talloc_free(tlspacket); return NULL; } + memcpy(tlspacket->data, data, data_len); } @@ -784,7 +849,7 @@ static fr_tls_status_t eaptls_operation(fr_tls_status_t status, eap_handler_t *h * TLS proper can decide what to do, then. */ if (tls_session->dirty_out.used > 0) { - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); return FR_TLS_HANDLED; } @@ -890,7 +955,7 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler) * of fragments" phase. */ case FR_TLS_REQUEST: - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); status = FR_TLS_HANDLED; goto done; diff --git a/src/modules/rlm_eap/libeap/eap_tls.h b/src/modules/rlm_eap/libeap/eap_tls.h index 8e5fc77..1112bcb 100644 --- a/src/modules/rlm_eap/libeap/eap_tls.h +++ b/src/modules/rlm_eap/libeap/eap_tls.h @@ -59,10 +59,14 @@ fr_tls_status_t eaptls_process(eap_handler_t *handler); int eaptls_success(eap_handler_t *handler, int peap_flag) CC_HINT(nonnull); int eaptls_fail(eap_handler_t *handler, int peap_flag) CC_HINT(nonnull); -int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn) CC_HINT(nonnull); +int eaptls_request(EAP_DS *eap_ds, tls_session_t *ssn, bool start) CC_HINT(nonnull); +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +void TLS_PRF(SSL *ssl, unsigned char *sec, size_t seclen, struct iovec *iov, size_t iovcnt, unsigned char *key, size_t keylen); +#endif void T_PRF(unsigned char const *secret, unsigned int secret_len, char const *prf_label, unsigned char const *seed, unsigned int seed_len, unsigned char *out, unsigned int out_len) CC_HINT(nonnull(1,3,6)); +void eaptls_gen_keys_only(REQUEST *request, SSL *s, char const *label, uint8_t const *context, UNUSED size_t context_size, uint8_t *out, size_t outlen); void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t const *context, size_t context_size); void eapttls_gen_challenge(SSL *s, uint8_t *buffer, size_t size); void eaptls_gen_eap_key(eap_handler_t *handler); diff --git a/src/modules/rlm_eap/libeap/eap_types.h b/src/modules/rlm_eap/libeap/eap_types.h index c6568ff..beee998 100644 --- a/src/modules/rlm_eap/libeap/eap_types.h +++ b/src/modules/rlm_eap/libeap/eap_types.h @@ -98,7 +98,9 @@ typedef enum eap_method { PW_EAP_GPSK, /* 51 */ PW_EAP_PWD, /* 52 */ PW_EAP_EKE, /* 53 */ - PW_EAP_MAX_TYPES /* 54 - for validation */ + PW_EAP_PT_EAP, /* 54 */ + PW_EAP_TEAP, /* 55 */ + PW_EAP_MAX_TYPES /* 56 - for validation */ } eap_type_t; #define PW_EAP_EXPANDED_TYPE (254) diff --git a/src/modules/rlm_eap/libeap/eapcommon.c b/src/modules/rlm_eap/libeap/eapcommon.c index 96db30b..5abe47a 100644 --- a/src/modules/rlm_eap/libeap/eapcommon.c +++ b/src/modules/rlm_eap/libeap/eapcommon.c @@ -302,6 +302,7 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps) uint16_t len; int total_len; vp_cursor_t cursor; + bool allow_o = false; /* * Get only EAP-Message attribute list @@ -315,7 +316,7 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps) /* * Sanity check the length before doing anything. */ - if (first->vp_length < 4) { + if (first->vp_length < 5) { fr_strerror_printf("EAP packet is too short"); return NULL; } @@ -330,8 +331,8 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps) /* * Take out even more weird things. */ - if (len < 4) { - fr_strerror_printf("EAP packet has invalid length (less than 4 bytes)"); + if (len < 5) { + fr_strerror_printf("EAP packet has invalid length (less than 5 bytes)"); return NULL; } @@ -379,6 +380,125 @@ eap_packet_raw_t *eap_vp2packet(TALLOC_CTX *ctx, VALUE_PAIR *vps) ptr += i->vp_length; } + /* + * Do additional sanity check for TLS-based EAP types, so + * that we don't have to do any of that later. + */ + switch (eap_packet->data[0]) { + /* + * 0 1 2 3 + * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Code | Identifier | Length | + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * | Type | Flags | Ver | Message Length : + * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + * : Message Length | + * + * Flags are: + * + * 7 6 5 4 3 2 1 0 + * +-+-+-+-+-+-+-+-+ + * |L M S O R R R R| + * +-+-+-+-+-+-+-+-+ + * + * L = Length included + * M = More fragments + * S = EAP-TLS start + * O = outer TLV length included (4 octets, only for TEAP) + * R = Reserved + */ + case PW_EAP_TEAP: + allow_o = true; + /* FALL-THROUGH */ + + case PW_EAP_TLS: + case PW_EAP_TTLS: + case PW_EAP_PEAP: + case PW_EAP_FAST: + if (len < 2) { + fr_strerror_printf("Malformed EAP packet - packet is too small to contain TLS flags"); + talloc_free(eap_packet); + return NULL; + } + + /* + * L bit set means we have 4 octets of Length + * following the flags field. + */ + if ((eap_packet->data[1] & 0x80) != 0) { + uint32_t tls_len; + + if (len <= (2 + 4)) { + fr_strerror_printf("Malformed EAP packet - TLS 'L' bit is set, but packet is too small to contain 'length' field"); + talloc_free(eap_packet); + return NULL; + } + + /* + * This is arguably wrong... a single TLS + * record is max 16K in length. But a + * TLS message may span multiple TLS + * records. + */ + memcpy(&tls_len, eap_packet->data + 2, 4); + tls_len = ntohl(tls_len); + if (tls_len > 16384) { + fr_strerror_printf("Malformed EAP packet - TLS reassembled data length %u (%08x) (will be greater than the TLS maximum record size of 16384 bytes", tls_len, tls_len); + talloc_free(eap_packet); + return NULL; + } + + /* + * O bit set means we have 4 octets of Outer TLV Length + * following the Length field. + */ + if ((eap_packet->data[1] & 0x10) != 0) { + uint32_t tlv_len; + + if (!allow_o) { + fr_strerror_printf("Malformed EAP packet - TLS 'O' bit is set, but EAP method does not use it."); + talloc_free(eap_packet); + return NULL; + } + + if (len <= (2 + 4 + 4)) { + fr_strerror_printf("Malformed EAP packet - TLS 'O' bit is set, but packet is too small to contain 'outer tlv length' field"); + talloc_free(eap_packet); + return NULL; + } + + memcpy(&tlv_len, eap_packet->data + 2 + 4, 4); + tlv_len = ntohl(tlv_len); + + /* + * The EAP header includes all of + * the data in the packet. The + * outer TLV length cannot + * include the EAP header, type, + * flags, length field, or outer + * tlv length field. + */ + if ((int)tlv_len > (len - (2 + 4 + 4))) { + fr_strerror_printf("Malformed EAP packet - TLS 'O' bit is set, but 'outer tlv length' field is larger than the current fragment"); + talloc_free(eap_packet); + return NULL; + } + } + } else { + if ((eap_packet->data[1] & 0x10) != 0) { + fr_strerror_printf("Malformed EAP packet - TLS 'O' bit is set, but 'L' bit is not set."); + talloc_free(eap_packet); + return NULL; + } + + } + break; + + default: + break; + } + return eap_packet; } diff --git a/src/modules/rlm_eap/libeap/mppe_keys.c b/src/modules/rlm_eap/libeap/mppe_keys.c index 385441c..4356164 100644 --- a/src/modules/rlm_eap/libeap/mppe_keys.c +++ b/src/modules/rlm_eap/libeap/mppe_keys.c @@ -34,6 +34,35 @@ USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ #include <openssl/provider.h> #endif +#if OPENSSL_VERSION_NUMBER >= 0x10101000L +#include <openssl/kdf.h> + +void TLS_PRF(SSL *ssl, + unsigned char *sec, size_t seclen, + struct iovec *iov, size_t iovcnt, + unsigned char *key, size_t keylen) +{ + const EVP_MD *md = SSL_CIPHER_get_handshake_digest(SSL_get_current_cipher(ssl)); + EVP_MD *unconst_md; + EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_TLS1_PRF, NULL); + + EVP_PKEY_derive_init(pctx); + + memcpy(&unconst_md, &md, sizeof(md)); /* const issues */ + EVP_PKEY_CTX_set_tls1_prf_md(pctx, unconst_md); + + EVP_PKEY_CTX_set1_tls1_prf_secret(pctx, sec, seclen); + + for (unsigned int i = 0; i < iovcnt; i++) { + EVP_PKEY_CTX_add1_tls1_prf_seed(pctx, iov[i].iov_base, iov[i].iov_len); + } + + EVP_PKEY_derive(pctx, key, &keylen); + + EVP_PKEY_CTX_free(pctx); +} +#endif + /* * TLS P_hash from RFC 2246/5246 section 5 */ @@ -210,23 +239,20 @@ void T_PRF(unsigned char const *secret, unsigned int secret_len, #define EAPTLS_MPPE_KEY_LEN 32 /* - * Generate keys according to RFC 2716 and add to reply + * Generate keys according to RFC 5216 (section 2.3) */ -void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t const *context, UNUSED size_t context_size) +void eaptls_gen_keys_only(UNUSED REQUEST *request, SSL *s, char const *label, uint8_t const *context, UNUSED size_t context_size, uint8_t *out, size_t outlen) { - uint8_t out[4 * EAPTLS_MPPE_KEY_LEN]; - uint8_t *p; - size_t len; - - len = strlen(label); + size_t len = strlen(label); #if OPENSSL_VERSION_NUMBER >= 0x10001000L - if (SSL_export_keying_material(s, out, sizeof(out), label, len, context, context_size, context != NULL) != 1) { + if (SSL_export_keying_material(s, out, outlen, label, len, context, context_size, context != NULL) != 1) { ERROR("Failed generating keying material"); return; } #else { + uint8_t *p; uint8_t seed[64 + (2 * SSL3_RANDOM_SIZE) + (context ? 2 + context_size : 0)]; uint8_t buf[4 * EAPTLS_MPPE_KEY_LEN]; @@ -255,9 +281,20 @@ void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t c } PRF(s->session->master_key, s->session->master_key_length, - seed, len, out, buf, sizeof(out)); + seed, len, out, buf, outlen); } #endif +} + +/* + * Generate keys according to RFC 5216 (section 2.3) and add to reply + */ +void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t const *context, UNUSED size_t context_size) +{ + uint8_t out[4 * EAPTLS_MPPE_KEY_LEN]; + uint8_t *p; + + eaptls_gen_keys_only(request, s, label, context, context_size, out, sizeof(out)); p = out; eap_add_reply(request, "MS-MPPE-Recv-Key", p, EAPTLS_MPPE_KEY_LEN); @@ -268,7 +305,6 @@ void eaptls_gen_mppe_keys(REQUEST *request, SSL *s, char const *label, uint8_t c eap_add_reply(request, "EAP-EMSK", out + 64, 64); } - #define FR_TLS_PRF_CHALLENGE "ttls challenge" /* diff --git a/src/modules/rlm_eap/mem.c b/src/modules/rlm_eap/mem.c index 6be8ca4..680c879 100644 --- a/src/modules/rlm_eap/mem.c +++ b/src/modules/rlm_eap/mem.c @@ -38,6 +38,9 @@ RCSID("$Id$") #define PTHREAD_MUTEX_UNLOCK(_x) #endif +static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request, + eap_handler_t *handler, char const *msg, bool delete); + /* * Allocate a new eap_packet_t */ @@ -78,11 +81,6 @@ void eap_ds_free(EAP_DS **eap_ds_p) static int _eap_handler_free(eap_handler_t *handler) { - if (handler->identity) { - talloc_free(handler->identity); - handler->identity = NULL; - } - if (handler->prev_eapds) eap_ds_free(&(handler->prev_eapds)); if (handler->eap_ds) eap_ds_free(&(handler->eap_ds)); @@ -124,9 +122,10 @@ static int _eap_handler_free(eap_handler_t *handler) /* * Allocate a new eap_handler_t */ -eap_handler_t *eap_handler_alloc(rlm_eap_t *inst) +eap_handler_t *eap_handler_alloc(rlm_eap_t *inst, REQUEST *request) { - eap_handler_t *handler; + eap_handler_t *handler, *old; + char buffer[256]; handler = talloc_zero(NULL, eap_handler_t); if (!handler) { @@ -138,6 +137,20 @@ eap_handler_t *eap_handler_alloc(rlm_eap_t *inst) /* Doesn't need to be inside the critical region */ talloc_set_destructor(handler, _eap_handler_free); + if (!inst->dedup_tree) return handler; + + if (radius_xlat(buffer, sizeof(buffer), request, inst->dedup_key, NULL, NULL) < 0) return handler; + + handler->dedup = talloc_strdup(handler, buffer); + + /* + * Delete any old handler + */ + PTHREAD_MUTEX_LOCK(&(inst->session_mutex)); + old = rbtree_finddata(inst->dedup_tree, handler); + if (old) (void) eaplist_delete(inst, request, old, "Cancelling", true); + PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex)); + return handler; } @@ -172,21 +185,24 @@ static uint32_t eap_rand(fr_randctx *ctx) static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request, - eap_handler_t *handler) + eap_handler_t *handler, char const *msg, bool delete) { rbnode_t *node; + if (delete && inst->dedup_tree) (void) rbtree_deletebydata(inst->dedup_tree, handler); + node = rbtree_find(inst->session_tree, handler); if (!node) return NULL; handler = rbtree_node2data(inst->session_tree, node); - RDEBUG("Finished EAP session with state " - "0x%02x%02x%02x%02x%02x%02x%02x%02x", + RDEBUG("%s EAP session with state " + "0x%02x%02x%02x%02x%02x%02x%02x%02x", msg, handler->state[0], handler->state[1], handler->state[2], handler->state[3], handler->state[4], handler->state[5], handler->state[6], handler->state[7]); + /* * Delete old handler from the tree. */ @@ -207,7 +223,28 @@ static eap_handler_t *eaplist_delete(rlm_eap_t *inst, REQUEST *request, } handler->prev = handler->next = NULL; - return handler; + if (!delete) return handler; + + +#ifdef WITH_TLS + /* + * Remove expired TLS sessions. + */ + switch (handler->type) { + case PW_EAP_TLS: + case PW_EAP_TTLS: + case PW_EAP_PEAP: + case PW_EAP_FAST: + tls_fail(handler->opaque); /* MUST be a tls_session! */ + break; + + default: + break; + } +#endif + + talloc_free(handler); + return NULL; } @@ -227,52 +264,12 @@ static void eaplist_expire(rlm_eap_t *inst, REQUEST *request, time_t timestamp) handler = inst->session_head; if (!handler) break; - RDEBUG("Expiring EAP session with state " - "0x%02x%02x%02x%02x%02x%02x%02x%02x", - handler->state[0], handler->state[1], - handler->state[2], handler->state[3], - handler->state[4], handler->state[5], - handler->state[6], handler->state[7]); - /* * Expire entries from the start of the list. * They should be the oldest ones. */ if ((timestamp - handler->timestamp) > (int)inst->timer_limit) { - rbnode_t *node; - node = rbtree_find(inst->session_tree, handler); - rad_assert(node != NULL); - rbtree_delete(inst->session_tree, node); - - /* - * handler == inst->session_head - */ - inst->session_head = handler->next; - if (handler->next) { - handler->next->prev = NULL; - } else { - inst->session_head = NULL; - inst->session_tail = NULL; - } - -#ifdef WITH_TLS - /* - * Remove expired TLS sessions. - */ - switch (handler->type) { - case PW_EAP_TLS: - case PW_EAP_TTLS: - case PW_EAP_PEAP: - case PW_EAP_FAST: - tls_fail(handler->opaque); /* MUST be a tls_session! */ - break; - - default: - break; - } -#endif - - talloc_free(handler); + (void) eaplist_delete(inst, request, handler, "Expiring", true); } else { break; } @@ -347,7 +344,9 @@ int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler) handler->state[4] = handler->trips ^ handler->state[0]; handler->state[5] = handler->eap_id ^ handler->state[1]; handler->state[6] = handler->type ^ handler->state[2]; - handler->state[12] = handler->state[2] ^ (RADIUSD_VERSION & 0xff); + handler->state[8] = handler->state[2] ^ (((uint32_t) HEXIFY(RADIUSD_VERSION)) & 0xff); + handler->state[10] = handler->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 8) & 0xff); + handler->state[12] = handler->state[2] ^ ((((uint32_t) HEXIFY(RADIUSD_VERSION)) >> 16) & 0xff); fr_pair_value_memcpy(state, handler->state, sizeof(handler->state)); @@ -452,7 +451,7 @@ eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request, eaplist_expire(inst, request, request->timestamp); - handler = eaplist_delete(inst, request, &myHandler); + handler = eaplist_delete(inst, request, &myHandler, "Removing", false); PTHREAD_MUTEX_UNLOCK(&(inst->session_mutex)); /* diff --git a/src/modules/rlm_eap/rlm_eap.c b/src/modules/rlm_eap/rlm_eap.c index efb9660..18eb1c4 100644 --- a/src/modules/rlm_eap/rlm_eap.c +++ b/src/modules/rlm_eap/rlm_eap.c @@ -38,7 +38,9 @@ static const CONF_PARSER module_config[] = { { "max_eap_type", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, max_eap_type), "52" }, { "ignore_unknown_eap_types", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, ignore_unknown_types), "no" }, { "cisco_accounting_username_bug", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, mod_accounting_username_bug), "no" }, + { "allow_empty_identities", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_t, allow_empty_identities), NULL }, { "max_sessions", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_t, max_sessions), "2048" }, + { "dedup_key", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_t, dedup_key), "" }, CONF_PARSER_TERMINATOR }; @@ -98,6 +100,21 @@ static int eap_handler_cmp(void const *a, void const *b) return 0; } +/* + * Compare two handlers by dedup key + */ +static int CC_HINT(nonnull) dedup_cmp(void const *a, void const *b) +{ + eap_handler_t const *one = a; + eap_handler_t const *two = b; + + if (!one->dedup && two->dedup) return -1; + if (one->dedup && !two->dedup) return +1; + + if (!one->dedup && !two->dedup) return 0; + + return strcmp(one->dedup, two->dedup); +} /* * read the config section and load all the eap authentication types present. @@ -185,6 +202,7 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance) case PW_EAP_TTLS: case PW_EAP_PEAP: case PW_EAP_PWD: + case PW_EAP_TEAP: WARN("rlm_eap (%s): Ignoring EAP method %s because we don't have OpenSSL support", inst->xlat_name, name); continue; @@ -255,6 +273,16 @@ static int mod_instantiate(CONF_SECTION *cs, void *instance) } #endif + if (!inst->dedup_key || !*inst->dedup_key) { + return 0; + } + + inst->dedup_tree = rbtree_create(NULL, dedup_cmp, NULL, 0); + if (!inst->dedup_tree) { + ERROR("rlm_eap (%s): Cannot initialize dedup tree", inst->xlat_name); + return -1; + } + return 0; } @@ -560,19 +588,16 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *reque #ifdef WITH_PROXY -static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *request) +static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(UNUSED void *instance, REQUEST *request) { VALUE_PAIR *vp; - size_t length; - rlm_eap_t *inst = instance; + size_t length, eap_length; vp = fr_pair_find_by_num(request->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY); if (!vp) return RLM_MODULE_NOOP; - if (vp->vp_length < 4) return RLM_MODULE_NOOP; - - if ((vp->vp_octets[0] == 0) ||( vp->vp_octets[0] > 6)) { - RDEBUG("EAP header byte zero has invalid value"); + if (vp->vp_length < 4) { + RDEBUG("EAP packet is too small"); add_error_cause: /* @@ -582,21 +607,20 @@ static rlm_rcode_t CC_HINT(nonnull) mod_pre_proxy(void *instance, REQUEST *reque return RLM_MODULE_REJECT; } + /* + * The length field has to match the length of all EAP-Messages. + */ length = (vp->vp_octets[2] << 8) | vp->vp_octets[3]; - if (length != vp->vp_length) { - RDEBUG("EAP length does not match attribute length"); - return RLM_MODULE_REJECT; - } - if (vp->vp_octets[0] != PW_EAP_REQUEST) return RLM_MODULE_NOOP; - if (!inst->max_eap_type) return RLM_MODULE_NOOP; - - if (vp->vp_length < 5) return RLM_MODULE_NOOP; - - if (vp->vp_octets[4] == 254) return RLM_MODULE_NOOP; /* allow extended types */ + /* + * Get length of all EAP-Message attributes + */ + for (eap_length = 0; vp != NULL; vp = vp->next) { + eap_length += vp->vp_length; + } - if (vp->vp_octets[4] > inst->max_eap_type) { - RDEBUG("EAP method %u is too large", vp->vp_octets[4]); + if (length != eap_length) { + RDEBUG("EAP length does not match attribute length"); goto add_error_cause; } diff --git a/src/modules/rlm_eap/rlm_eap.h b/src/modules/rlm_eap/rlm_eap.h index 0b9311c..4d5e1fa 100644 --- a/src/modules/rlm_eap/rlm_eap.h +++ b/src/modules/rlm_eap/rlm_eap.h @@ -63,14 +63,19 @@ typedef struct rlm_eap { bool ignore_unknown_types; bool mod_accounting_username_bug; + bool allow_empty_identities; uint32_t max_sessions; + char const *dedup_key; + #ifdef HAVE_PTHREAD_H pthread_mutex_t session_mutex; pthread_mutex_t handler_mutex; #endif + rbtree_t *dedup_tree; + char const *xlat_name; /* no xlat's yet */ fr_randctx rand_pool; } rlm_eap_t; @@ -102,7 +107,7 @@ eap_handler_t *eap_handler(rlm_eap_t *inst, eap_packet_raw_t **eap_msg, REQUEST /* Memory Management */ EAP_DS *eap_ds_alloc(eap_handler_t *handler); -eap_handler_t *eap_handler_alloc(rlm_eap_t *inst); +eap_handler_t *eap_handler_alloc(rlm_eap_t *inst, REQUEST *request); void eap_ds_free(EAP_DS **eap_ds); int eaplist_add(rlm_eap_t *inst, eap_handler_t *handler) CC_HINT(nonnull); eap_handler_t *eaplist_find(rlm_eap_t *inst, REQUEST *request, eap_packet_raw_t *eap_packet); diff --git a/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c b/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c index 093dc86..d1f45b7 100644 --- a/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c +++ b/src/modules/rlm_eap/types/rlm_eap_fast/rlm_eap_fast.c @@ -479,7 +479,7 @@ static int mod_process(void *arg, eap_handler_t *handler) case PW_CODE_ACCESS_CHALLENGE: RDEBUG("Challenge"); tls_handshake_send(request, tls_session); - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); ret = 1; goto done; diff --git a/src/modules/rlm_eap/types/rlm_eap_peap/peap.c b/src/modules/rlm_eap/types/rlm_eap_peap/peap.c index a8589ae..efe9b10 100644 --- a/src/modules/rlm_eap/types/rlm_eap_peap/peap.c +++ b/src/modules/rlm_eap/types/rlm_eap_peap/peap.c @@ -591,8 +591,6 @@ static int CC_HINT(nonnull) eappeap_postproxy(eap_handler_t *handler, void *data fprintf(fr_log_fp, "server %s {\n", fake->server); } - fake->reply->code = PW_CODE_ACCESS_ACCEPT; - /* * Perform a post-auth stage, which will get the EAP * handler, too... @@ -622,6 +620,8 @@ static int CC_HINT(nonnull) eappeap_postproxy(eap_handler_t *handler, void *data request->proxy_reply = talloc_steal(request, fake->reply); fake->reply = NULL; + request->proxy->dst_port = 0; /* hacks for state.c lookups */ + /* * And we're done with this request. */ @@ -667,7 +667,7 @@ static int CC_HINT(nonnull) eappeap_postproxy(eap_handler_t *handler, void *data case RLM_MODULE_HANDLED: RDEBUG2("Reply was handled"); - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); request->proxy_reply->code = PW_CODE_ACCESS_CHALLENGE; return 1; diff --git a/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c b/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c index d9f850c..704b86c 100644 --- a/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c +++ b/src/modules/rlm_eap/types/rlm_eap_peap/rlm_eap_peap.c @@ -233,7 +233,7 @@ static int mod_session_init(void *type_arg, eap_handler_t *handler) * TLS session initialization is over. Now handle TLS * related handshaking or application data. */ - status = eaptls_start(handler->eap_ds, ssn->peap_flag); + status = eaptls_request(handler->eap_ds, ssn, true); if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); } else { @@ -357,7 +357,7 @@ static int mod_process(void *arg, eap_handler_t *handler) goto done; case RLM_MODULE_HANDLED: - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); ret = 1; goto done; diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/.gitignore b/src/modules/rlm_eap/types/rlm_eap_teap/.gitignore new file mode 100644 index 0000000..01a5daa --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/.gitignore @@ -0,0 +1 @@ +all.mk diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/all.mk.in b/src/modules/rlm_eap/types/rlm_eap_teap/all.mk.in new file mode 100644 index 0000000..dfdcd71 --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/all.mk.in @@ -0,0 +1,12 @@ +TARGETNAME := @targetname@ + +ifneq "$(OPENSSL_LIBS)" "" +ifneq "$(TARGETNAME)" "" +TARGET := $(TARGETNAME).a +endif +endif + +SOURCES := $(TARGETNAME).c eap_teap.c eap_teap_crypto.c + +SRC_INCDIRS := ../../ ../../libeap/ +TGT_PREREQS := libfreeradius-eap.a diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/configure b/src/modules/rlm_eap/types/rlm_eap_teap/configure new file mode 100755 index 0000000..e37094d --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/configure @@ -0,0 +1,4512 @@ +#! /bin/sh +# From configure.ac Revision. +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.69. +# +# +# Copyright (C) 1992-1996, 1998-2012 Free Software Foundation, Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +as_fn_exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : + +else + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 +test \$(( 1 + 1 )) = 2 || exit 1" + if (eval "$as_required") 2>/dev/null; then : + as_have_required=yes +else + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : + +else + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir/$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : + CONFIG_SHELL=$as_shell as_have_required=yes + if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : + break 2 +fi +fi + done;; + esac + as_found=false +done +$as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi; } +IFS=$as_save_IFS + + + if test "x$CONFIG_SHELL" != x; then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +$as_echo "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno; then : + $as_echo "$0: This script requires a shell more modern than all" + $as_echo "$0: the shells that I found on your system." + if test x${ZSH_VERSION+set} = xset ; then + $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" + $as_echo "$0: be upgraded to zsh 4.3.4 or later." + else + $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, +$0: including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 </dev/null +exec 6>&1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME= +PACKAGE_TARNAME= +PACKAGE_VERSION= +PACKAGE_STRING= +PACKAGE_BUGREPORT= +PACKAGE_URL= + +ac_unique_file="rlm_eap_teap.c" +ac_subst_vars='LTLIBOBJS +LIBOBJS +mod_cflags +mod_ldflags +targetname +EGREP +GREP +CPP +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +with_rlm_eap_teap +with_openssl_lib_dir +with_openssl_include_dir +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS +CPP' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + # Accept the important Cygnus configure options, so we can diagnose typos. + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: $ac_useropt" + ac_useropt_orig=$ac_useropt + ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures this package to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/PACKAGE] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + + cat <<\_ACEOF + +Optional Packages: + --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] + --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) + --without-rlm_eap_teap build without rlm_eap_teap + --with-openssl-lib-dir=DIR + directory for LDAP library files + -with-openssl-include-dir=DIR + directory for LDAP include files + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a + nonstandard directory <lib dir> + LIBS libraries to pass to the linker, e.g. -l<library> + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I<include dir> if + you have headers in a nonstandard directory <include dir> + CPP C preprocessor + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to the package provider. +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for guested configure. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +configure +generated by GNU Autoconf 2.69 + +Copyright (C) 2012 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +echo +echo Running tests for rlm_eap_teap +echo + + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_try_cpp LINENO +# ---------------------- +# Try to preprocess conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_cpp () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + if { { ac_try="$ac_cpp conftest.$ac_ext" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } > conftest.i && { + test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || + test ! -s conftest.err + }; then : + ac_retval=0 +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_cpp +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + $ $0 $@ + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + $as_echo "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Save into config.log some information that might help in debugging. + { + echo + + $as_echo "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + $as_echo "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + $as_echo "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + $as_echo "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + $as_echo "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + $as_echo "$as_me: caught signal $ac_signal" + $as_echo "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +$as_echo "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_NAME "$PACKAGE_NAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_TARNAME "$PACKAGE_TARNAME" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_VERSION "$PACKAGE_VERSION" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_STRING "$PACKAGE_STRING" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" +_ACEOF + +cat >>confdefs.h <<_ACEOF +#define PACKAGE_URL "$PACKAGE_URL" +_ACEOF + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +ac_site_file1=NONE +ac_site_file2=NONE +if test -n "$CONFIG_SITE"; then + # We do not want a PATH search for config.site. + case $CONFIG_SITE in #(( + -*) ac_site_file1=./$CONFIG_SITE;; + */*) ac_site_file1=$CONFIG_SITE;; + *) ac_site_file1=./$CONFIG_SITE;; + esac +elif test "x$prefix" != xNONE; then + ac_site_file1=$prefix/share/config.site + ac_site_file2=$prefix/etc/config.site +else + ac_site_file1=$ac_default_prefix/share/config.site + ac_site_file2=$ac_default_prefix/etc/config.site +fi +for ac_site_file in "$ac_site_file1" "$ac_site_file2" +do + test "x$ac_site_file" = xNONE && continue + if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +$as_echo "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +$as_echo "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +$as_echo "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +$as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +$as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +$as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +$as_echo "$as_me: former value: \`$ac_old_val'" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +$as_echo "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +$as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + +# Check whether --with-rlm_eap_teap was given. +if test "${with_rlm_eap_teap+set}" = set; then : + withval=$with_rlm_eap_teap; +fi + + + +mod_ldflags= +mod_cflags= + + +fail= +fr_status= +fr_features= +: > "config.report" +: > "config.report.tmp" + + + +if test x"$with_rlm_eap_teap" != xno; then + + +openssl_lib_dir= + +# Check whether --with-openssl-lib-dir was given. +if test "${with_openssl_lib_dir+set}" = set; then : + withval=$with_openssl_lib_dir; case "$withval" in + no) + as_fn_error $? "Need openssl-lib-dir" "$LINENO" 5 + ;; + yes) + ;; + *) + openssl_lib_dir="$withval" + ;; + esac +fi + + +openssl_include_dir= + +# Check whether --with-openssl-include-dir was given. +if test "${with_openssl_include_dir+set}" = set; then : + withval=$with_openssl_include_dir; case "$withval" in + no) + as_fn_error $? "Need openssl-include-dir" "$LINENO" 5 + ;; + yes) + ;; + *) + openssl_include_dir="$withval" + ;; + esac +fi + + + +smart_try_dir=$openssl_include_dir +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +$as_echo "$CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +$as_echo_n "checking for $ac_word... " >&6; } +if ${ac_cv_prog_ac_ct_CC+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir/$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +$as_echo "$ac_ct_CC" >&6; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +$as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi + + +test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +$as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +$as_echo_n "checking whether the C compiler works... " >&6; } +ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else + ac_file='' +fi +if test -z "$ac_file"; then : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +$as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +$as_echo_n "checking for C compiler default output file name... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +$as_echo "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +$as_echo_n "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +$as_echo "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdio.h> +int +main () +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +$as_echo_n "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +$as_echo "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +$as_echo_n "checking for suffix of object files... " >&6; } +if ${ac_cv_objext+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +$as_echo "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else + $as_echo "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +$as_echo "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 +$as_echo_n "checking whether we are using the GNU C compiler... " >&6; } +if ${ac_cv_c_compiler_gnu+:} false; then : + $as_echo_n "(cached) " >&6 +else + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_compiler_gnu=yes +else + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +$as_echo "$ac_cv_c_compiler_gnu" >&6; } +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+set} +ac_save_CFLAGS=$CFLAGS +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +$as_echo_n "checking whether $CC accepts -g... " >&6; } +if ${ac_cv_prog_cc_g+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +else + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + +else + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main () +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +$as_echo "$ac_cv_prog_cc_g" >&6; } +if test "$ac_test_CFLAGS" = set; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 +$as_echo_n "checking for $CC option to accept ISO C89... " >&6; } +if ${ac_cv_prog_cc_c89+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <stdarg.h> +#include <stdio.h> +struct stat; +/* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ +struct buf { int x; }; +FILE * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not '\xHH' hex character constants. + These don't provoke an error unfortunately, instead are silently treated + as 'x'. The following induces an error, until -std is added to get + proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an + array size at least. It's necessary to write '\x00'==0 to get something + that's true only with -std. */ +int osf4_cc_array ['\x00' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) 'x' +int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); +int argc; +char **argv; +int +main () +{ +return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; + ; + return 0; +} +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ + -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO"; then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC + +fi +# AC_CACHE_VAL +case "x$ac_cv_prog_cc_c89" in + x) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +$as_echo "none needed" >&6; } ;; + xno) + { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +$as_echo "unsupported" >&6; } ;; + *) + CC="$CC $ac_cv_prog_cc_c89" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +$as_echo "$ac_cv_prog_cc_c89" >&6; } ;; +esac +if test "x$ac_cv_prog_cc_c89" != xno; then : + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + +ac_safe=`echo "openssl/ec.h" | sed 'y%./+-%__pm%'` +old_CPPFLAGS="$CPPFLAGS" +smart_include= +smart_include_dir="/usr/local/include /opt/include" + +_smart_try_dir= +_smart_include_dir= + +for _prefix in $smart_prefix ""; do + for _dir in $smart_try_dir; do + _smart_try_dir="${_smart_try_dir} ${_dir}/${_prefix}" + done + + for _dir in $smart_include_dir; do + _smart_include_dir="${_smart_include_dir} ${_dir}/${_prefix}" + done +done + +if test "x$_smart_try_dir" != "x"; then + for try in $_smart_try_dir; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ec.h in $try" >&5 +$as_echo_n "checking for openssl/ec.h in $try... " >&6; } + CPPFLAGS="-isystem $try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <openssl/ec.h> +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include="-isystem $try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_include" = "x"; then + for _prefix in $smart_prefix; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ${_prefix}/openssl/ec.h" >&5 +$as_echo_n "checking for ${_prefix}/openssl/ec.h... " >&6; } + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <openssl/ec.h> +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include="-isystem ${_prefix}/" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done +fi + +if test "x$smart_include" = "x"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ec.h" >&5 +$as_echo_n "checking for openssl/ec.h... " >&6; } + + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <openssl/ec.h> +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include=" " + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext +fi + +if test "x$smart_include" = "x"; then + + for try in $_smart_include_dir; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for openssl/ec.h in $try" >&5 +$as_echo_n "checking for openssl/ec.h in $try... " >&6; } + CPPFLAGS="-isystem $try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + + #include <openssl/ec.h> +int +main () +{ +int a = 1; + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO"; then : + + smart_include="-isystem $try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + + smart_include= + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fi +rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext + done + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_include" != "x"; then + eval "ac_cv_header_$ac_safe=yes" + CPPFLAGS="$smart_include $old_CPPFLAGS" + SMART_CPPFLAGS="$smart_include $SMART_CPPFLAGS" +fi + +smart_prefix= + +if test "$ac_cv_header_openssl_ec_h" != "yes"; then + +fail="$fail openssl/ec.h" + +fi + +smart_try_dir=$openssl_lib_dir + + +sm_lib_safe=`echo "crypto" | sed 'y%./+-%__p_%'` +sm_func_safe=`echo "EVP_CIPHER_CTX_new" | sed 'y%./+-%__p_%'` + +old_LIBS="$LIBS" +old_CPPFLAGS="$CPPFLAGS" +smart_lib= +smart_ldflags= +smart_lib_dir= + +if test "x$smart_try_dir" != "x"; then + for try in $smart_try_dir; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_CIPHER_CTX_new in -lcrypto in $try" >&5 +$as_echo_n "checking for EVP_CIPHER_CTX_new in -lcrypto in $try... " >&6; } + LIBS="-lcrypto $old_LIBS" + CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +extern char EVP_CIPHER_CTX_new(); +int +main () +{ +EVP_CIPHER_CTX_new() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + smart_lib="-lcrypto" + smart_ldflags="-L$try -Wl,-rpath,$try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + LIBS="$old_LIBS" + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_lib" = "x"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_CIPHER_CTX_new in -lcrypto" >&5 +$as_echo_n "checking for EVP_CIPHER_CTX_new in -lcrypto... " >&6; } + LIBS="-lcrypto $old_LIBS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +extern char EVP_CIPHER_CTX_new(); +int +main () +{ +EVP_CIPHER_CTX_new() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + smart_lib="-lcrypto" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + LIBS="$old_LIBS" +fi + +if test "x$smart_lib" = "x"; then + for try in /usr/local/lib /opt/lib; do + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for EVP_CIPHER_CTX_new in -lcrypto in $try" >&5 +$as_echo_n "checking for EVP_CIPHER_CTX_new in -lcrypto in $try... " >&6; } + LIBS="-lcrypto $old_LIBS" + CPPFLAGS="-L$try -Wl,-rpath,$try $old_CPPFLAGS" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +extern char EVP_CIPHER_CTX_new(); +int +main () +{ +EVP_CIPHER_CTX_new() + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + + smart_lib="-lcrypto" + smart_ldflags="-L$try -Wl,-rpath,$try" + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + break + +else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext + done + LIBS="$old_LIBS" + CPPFLAGS="$old_CPPFLAGS" +fi + +if test "x$smart_lib" != "x"; then + eval "ac_cv_lib_${sm_lib_safe}_${sm_func_safe}=yes" + LIBS="$smart_ldflags $smart_lib $old_LIBS" + SMART_LIBS="$smart_ldflags $smart_lib $SMART_LIBS" +fi + +if test "x$ac_cv_lib_crypto_EVP_CIPHER_CTX_new" != "xyes"; then + +fail="$fail libssl" + +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 +$as_echo_n "checking how to run the C preprocessor... " >&6; } +# On Suns, sometimes $CPP names a directory. +if test -n "$CPP" && test -d "$CPP"; then + CPP= +fi +if test -z "$CPP"; then + if ${ac_cv_prog_CPP+:} false; then : + $as_echo_n "(cached) " >&6 +else + # Double quotes because CPP needs to be expanded + for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" + do + ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + break +fi + + done + ac_cv_prog_CPP=$CPP + +fi + CPP=$ac_cv_prog_CPP +else + ac_cv_prog_CPP=$CPP +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 +$as_echo "$CPP" >&6; } +ac_preproc_ok=false +for ac_c_preproc_warn_flag in '' yes +do + # Use a header file that comes with gcc, so configuring glibc + # with a fresh cross-compiler works. + # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since + # <limits.h> exists even on freestanding compilers. + # On the NeXT, cc -E runs the code through the compiler's parser, + # not just through cpp. "Syntax error" is here to catch this case. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#ifdef __STDC__ +# include <limits.h> +#else +# include <assert.h> +#endif + Syntax error +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + +else + # Broken: fails on valid input. +continue +fi +rm -f conftest.err conftest.i conftest.$ac_ext + + # OK, works on sane cases. Now check whether nonexistent headers + # can be detected and how. + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <ac_nonexistent.h> +_ACEOF +if ac_fn_c_try_cpp "$LINENO"; then : + # Broken: success on invalid input. +continue +else + # Passes both tests. +ac_preproc_ok=: +break +fi +rm -f conftest.err conftest.i conftest.$ac_ext + +done +# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. +rm -f conftest.i conftest.err conftest.$ac_ext +if $ac_preproc_ok; then : + +else + { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +$as_echo "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "C preprocessor \"$CPP\" fails sanity check +See \`config.log' for more details" "$LINENO" 5; } +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 +$as_echo_n "checking for grep that handles long lines and -e... " >&6; } +if ${ac_cv_path_GREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if test -z "$GREP"; then + ac_path_GREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in grep ggrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_GREP" || continue +# Check for GNU ac_path_GREP and select it if it is found. + # Check for GNU $ac_path_GREP +case `"$ac_path_GREP" --version 2>&1` in +*GNU*) + ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'GREP' >> "conftest.nl" + "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_GREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_GREP="$ac_path_GREP" + ac_path_GREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_GREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_GREP"; then + as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_GREP=$GREP +fi + +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 +$as_echo "$ac_cv_path_GREP" >&6; } + GREP="$ac_cv_path_GREP" + + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 +$as_echo_n "checking for egrep... " >&6; } +if ${ac_cv_path_EGREP+:} false; then : + $as_echo_n "(cached) " >&6 +else + if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 + then ac_cv_path_EGREP="$GREP -E" + else + if test -z "$EGREP"; then + ac_path_EGREP_found=false + # Loop through the user's path and test for each of PROGNAME-LIST + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + for ac_prog in egrep; do + for ac_exec_ext in '' $ac_executable_extensions; do + ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" + as_fn_executable_p "$ac_path_EGREP" || continue +# Check for GNU ac_path_EGREP and select it if it is found. + # Check for GNU $ac_path_EGREP +case `"$ac_path_EGREP" --version 2>&1` in +*GNU*) + ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; +*) + ac_count=0 + $as_echo_n 0123456789 >"conftest.in" + while : + do + cat "conftest.in" "conftest.in" >"conftest.tmp" + mv "conftest.tmp" "conftest.in" + cp "conftest.in" "conftest.nl" + $as_echo 'EGREP' >> "conftest.nl" + "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break + diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break + as_fn_arith $ac_count + 1 && ac_count=$as_val + if test $ac_count -gt ${ac_path_EGREP_max-0}; then + # Best one so far, save it but keep looking for a better one + ac_cv_path_EGREP="$ac_path_EGREP" + ac_path_EGREP_max=$ac_count + fi + # 10*(2^10) chars as input seems more than enough + test $ac_count -gt 10 && break + done + rm -f conftest.in conftest.tmp conftest.nl conftest.out;; +esac + + $ac_path_EGREP_found && break 3 + done + done + done +IFS=$as_save_IFS + if test -z "$ac_cv_path_EGREP"; then + as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 + fi +else + ac_cv_path_EGREP=$EGREP +fi + + fi +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 +$as_echo "$ac_cv_path_EGREP" >&6; } + EGREP="$ac_cv_path_EGREP" + + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include <openssl/crypto.h> + #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) + yes + #endif + +_ACEOF +if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | + $EGREP "yes" >/dev/null 2>&1; then : + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL version >= 1.1.1" >&5 +$as_echo_n "checking for OpenSSL version >= 1.1.1... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for OpenSSL version >= 1.1.1" >&5 +$as_echo_n "checking for OpenSSL version >= 1.1.1... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + +fail="$fail OpenSSL>=1.1.1" + + + +fi +rm -f conftest* + + + + targetname=rlm_eap_teap +else + targetname= + echo \*\*\* module rlm_eap_teap is disabled. + + +fr_status="disabled" + +fi + +if test x"$fail" != x""; then + targetname="" + + + if test x"${enable_strict_dependencies}" = x"yes"; then + as_fn_error $? "set --without-rlm_eap_teap to disable it explicitly." "$LINENO" 5 + else + + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: silently not building rlm_eap_teap." >&5 +$as_echo "$as_me: WARNING: silently not building rlm_eap_teap." >&2;} + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: FAILURE: rlm_eap_teap requires: $fail." >&5 +$as_echo "$as_me: WARNING: FAILURE: rlm_eap_teap requires: $fail." >&2;}; + fail="$(echo $fail)" + + +fr_status="skipping (requires $fail)" + + fr_features= + + fi + +else + + +fr_status="OK" + +fi + +if test x"$fr_features" = x""; then + $as_echo "$fr_status" > "config.report" +else + $as_echo_n "$fr_status ... " > "config.report" + cat "config.report.tmp" >> "config.report" +fi + +rm "config.report.tmp" + + + + + + + +ac_config_files="$ac_config_files all.mk" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +$as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +$as_echo "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +$as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +# Transform confdefs.h into DEFS. +# Protect against shell expansion while executing Makefile rules. +# Protect against Makefile macro expansion. +# +# If the first sed substitution is executed (which looks for macros that +# take arguments), then branch to the quote section. Otherwise, +# look for a macro that doesn't take arguments. +ac_script=' +:mline +/\\$/{ + N + s,\\\n,, + b mline +} +t clear +:clear +s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g +t quote +s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g +t quote +b any +:quote +s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g +s/\[/\\&/g +s/\]/\\&/g +s/\$/$$/g +H +:any +${ + g + s/^\n// + s/\n/ /g + p +} +' +DEFS=`sed -n "$ac_script" confdefs.h` + + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`$as_echo "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +$as_echo "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + +as_nl=' +' +export as_nl +# Printing a long string crashes Solaris 7 /usr/bin/printf. +as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo +as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo +# Prefer a ksh shell builtin over an external printf program on Solaris, +# but without wasting forks for bash or zsh. +if test -z "$BASH_VERSION$ZSH_VERSION" \ + && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='print -r --' + as_echo_n='print -rn --' +elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then + as_echo='printf %s\n' + as_echo_n='printf %s' +else + if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then + as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' + as_echo_n='/usr/ucb/echo -n' + else + as_echo_body='eval expr "X$1" : "X\\(.*\\)"' + as_echo_n_body='eval + arg=$1; + case $arg in #( + *"$as_nl"*) + expr "X$arg" : "X\\(.*\\)$as_nl"; + arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; + esac; + expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" + ' + export as_echo_n_body + as_echo_n='sh -c $as_echo_n_body as_echo' + fi + export as_echo_body + as_echo='sh -c $as_echo_body as_echo' +fi + +# The user is always right. +if test "${PATH_SEPARATOR+set}" != set; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# IFS +# We need space, tab and new line, in precisely that order. Quoting is +# there to prevent editors from complaining about space-tab. +# (If _AS_PATH_WALK were called with IFS unset, it would disable word +# splitting by setting IFS to empty value.) +IFS=" "" $as_nl" + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + test -z "$as_dir" && as_dir=. + test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + +# Unset variables that we do not need and which cause bugs (e.g. in +# pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" +# suppresses any "Segmentation fault" message there. '((' could +# trigger a bug in pdksh 5.2.14. +for as_var in BASH_ENV ENV MAIL MAILPATH +do eval test x\${$as_var+set} = xset \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done +PS1='$ ' +PS2='> ' +PS4='+ ' + +# NLS nuisances. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# CDPATH. +(unset CDPATH) >/dev/null 2>&1 && unset CDPATH + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + $as_echo "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by $as_me, which was +generated by GNU Autoconf 2.69. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + +Configuration files: +$config_files + +Report bugs to the package provider." + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" +ac_cs_version="\\ +config.status +configured by $0, generated by GNU Autoconf 2.69, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2012 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + $as_echo "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + $as_echo "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h | --help | --hel | -h ) + $as_echo "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + $as_echo "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "all.mk") CONFIG_FILES="$CONFIG_FILES all.mk" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' </dev/null 2>/dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' <conf$$subs.awk | sed ' +/^[^""]/{ + N + s/\n// +} +' >>$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + + +eval set X " :F $CONFIG_FILES " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +$as_echo "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`$as_echo "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +$as_echo X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +$as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +$as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +$as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/configure.ac b/src/modules/rlm_eap/types/rlm_eap_teap/configure.ac new file mode 100644 index 0000000..6247f4c --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/configure.ac @@ -0,0 +1,86 @@ +AC_PREREQ([2.69]) +AC_INIT +AC_CONFIG_SRCDIR([rlm_eap_teap.c]) +AC_REVISION($Revision$) +FR_INIT_MODULE([rlm_eap_teap]) + +mod_ldflags= +mod_cflags= + +FR_MODULE_START_TESTS + +dnl ############################################################ +dnl # Check for command line options +dnl ############################################################ +dnl extra argument: --with-openssl-lib-dir +openssl_lib_dir= +AC_ARG_WITH(openssl-lib-dir, + [AS_HELP_STRING([--with-openssl-lib-dir=DIR], + [directory for LDAP library files])], + [case "$withval" in + no) + AC_MSG_ERROR(Need openssl-lib-dir) + ;; + yes) + ;; + *) + openssl_lib_dir="$withval" + ;; + esac]) + +dnl extra argument: --with-openssl-include-dir +openssl_include_dir= +AC_ARG_WITH(openssl-include-dir, + [AS_HELP_STRING([-with-openssl-include-dir=DIR], + [directory for LDAP include files])], + [case "$withval" in + no) + AC_MSG_ERROR(Need openssl-include-dir) + ;; + yes) + ;; + *) + openssl_include_dir="$withval" + ;; + esac]) + +dnl ############################################################ +dnl # Check for header files +dnl ############################################################ + +smart_try_dir=$openssl_include_dir +FR_SMART_CHECK_INCLUDE(openssl/ec.h) +if test "$ac_cv_header_openssl_ec_h" != "yes"; then + FR_MODULE_FAIL([openssl/ec.h]) +fi + +smart_try_dir=$openssl_lib_dir +FR_SMART_CHECK_LIB(crypto, EVP_CIPHER_CTX_new) +if test "x$ac_cv_lib_crypto_EVP_CIPHER_CTX_new" != "xyes"; then + FR_MODULE_FAIL([libssl]) +fi + +AC_EGREP_CPP(yes, + [#include <openssl/crypto.h> + #if (OPENSSL_VERSION_NUMBER >= 0x10101000L) + yes + #endif + ], + [ + AC_MSG_CHECKING([for OpenSSL version >= 1.1.1]) + AC_MSG_RESULT(yes) + ], + [ + AC_MSG_CHECKING([for OpenSSL version >= 1.1.1]) + AC_MSG_RESULT(no) + FR_MODULE_FAIL([OpenSSL>=1.1.1]) + ] +) + +FR_MODULE_END_TESTS + +AC_SUBST(mod_ldflags) +AC_SUBST(mod_cflags) + +AC_CONFIG_FILES([all.mk]) +AC_OUTPUT diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap.c b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap.c new file mode 100644 index 0000000..dbe694e --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap.c @@ -0,0 +1,1488 @@ +/* + * eap_teap.c contains the interfaces that are called from the main handler + * + * Version: $Id$ + * + * Copyright (C) 2022 Network RADIUS SARL <legal@networkradius.com> + * + * This software may not be redistributed in any form without the prior + * written consent of Network RADIUS. + * + * 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. + */ + +RCSID("$Id$") + +#include "eap_teap.h" +#include "eap_teap_crypto.h" +#include <freeradius-devel/sha1.h> +#include <openssl/ssl.h> +#include <openssl/rand.h> + +#define PW_EAP_TEAP_TLV_IDENTITY (PW_FREERADIUS_EAP_TEAP_TLV | (EAP_TEAP_TLV_IDENTITY << 8)) +#define PW_EAP_TEAP_TLV_PAC (PW_FREERADIUS_EAP_TEAP_TLV | (EAP_TEAP_TLV_PAC << 8)) + +#define EAPTLS_MPPE_KEY_LEN 32 + +#define RDEBUGHEX(_label, _data, _length) \ +do {\ + char __buf[8192];\ + for (size_t i = 0; (i < (size_t) _length) && (3*i < sizeof(__buf)); i++) {\ + sprintf(&__buf[3*i], " %02x", (uint8_t)(_data)[i]);\ + }\ + RDEBUG("%s - hexdump(len=%zu):%s", _label, (size_t)_length, __buf);\ +} while (0) + +#define RANDFILL(x) do { rad_assert(sizeof(x) % sizeof(uint32_t) == 0); for (size_t i = 0; i < sizeof(x); i += sizeof(uint32_t)) *((uint32_t *)&x[i]) = fr_rand(); } while(0) +#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) +#define MIN(a,b) (((a)>(b)) ? (b) : (a)) + +struct crypto_binding_buffer { + uint16_t tlv_type; + uint16_t length; + eap_tlv_crypto_binding_tlv_t binding; + uint8_t eap_type; + uint8_t outer_tlvs[1]; +} CC_HINT(__packed__); +#define CRYPTO_BINDING_BUFFER_INIT(_cbb) \ +do {\ + _cbb->tlv_type = htons(EAP_TEAP_TLV_MANDATORY | EAP_TEAP_TLV_CRYPTO_BINDING);\ + _cbb->length = htons(sizeof(struct eap_tlv_crypto_binding_tlv_t));\ + _cbb->eap_type = PW_EAP_TEAP;\ +} while (0) + +static struct teap_imck_t imck_zeros = { }; + +/** + * RFC 7170 EAP-TEAP Authentication Phase 1: Key Derivations + */ +static void eap_teap_init_keys(REQUEST *request, tls_session_t *tls_session) +{ + teap_tunnel_t *t = tls_session->opaque; + + const EVP_MD *md = SSL_CIPHER_get_handshake_digest(SSL_get_current_cipher(tls_session->ssl)); + const int md_type = EVP_MD_type(md); + + RDEBUG("Using MAC %s (%d)", OBJ_nid2sn(md_type), md_type); + + RDEBUG2("Deriving EAP-TEAP keys"); + + rad_assert(t->received_version > -1); + rad_assert(t->imckc == 0); + + /* S-IMCK[0] = session_key_seed (RFC7170, Section 5.1) */ + eaptls_gen_keys_only(request, tls_session->ssl, "EXPORTER: teap session key seed", NULL, 0, t->imck_msk.simck, sizeof(t->imck_msk.simck)); + memcpy(t->imck_emsk.simck, t->imck_msk.simck, sizeof(t->imck_msk.simck)); + RDEBUGHEX("S-IMCK[0]", t->imck_msk.simck, sizeof(t->imck_msk.simck)); +} + +/** + * RFC 7170 EAP-TEAP Intermediate Compound Key Derivations - Section 5.2 + */ +/** + * RFC 7170 - Intermediate Compound Key Derivations + */ +static void eap_teap_derive_imck(REQUEST *request, tls_session_t *tls_session, + uint8_t *msk, size_t msklen, + uint8_t *emsk, size_t emsklen) +{ + teap_tunnel_t *t = tls_session->opaque; + + t->imckc++; + RDEBUG2("Updating ICMK (j = %d)", t->imckc); + + uint8_t imsk_msk[EAP_TEAP_IMSK_LEN] = {0}; + uint8_t imsk_emsk[EAP_TEAP_IMSK_LEN + 32]; // +32 for EMSK overflow + struct teap_imck_t imck_msk, imck_emsk; + + uint8_t imck_label[27] = "Inner Methods Compound Keys"; // width trims trailing \0 + struct iovec imck_seed[2] = { + { (void *)imck_label, sizeof(imck_label) }, + { NULL, EAP_TEAP_IMSK_LEN } + }; + + if (msklen) { + memcpy(imsk_msk, msk, MIN(msklen, EAP_TEAP_IMSK_LEN)); + RDEBUGHEX("IMSK from MSK", imsk_msk, EAP_TEAP_IMSK_LEN); + } else { + RDEBUGHEX("IMSK Zero", imsk_msk, EAP_TEAP_IMSK_LEN); + } + imck_seed[1].iov_base = imsk_msk; + TLS_PRF(tls_session->ssl, + t->imck_msk.simck, sizeof(t->imck_msk.simck), + imck_seed, ARRAY_SIZE(imck_seed), + (uint8_t *)&imck_msk, sizeof(imck_msk)); + + /* IMCK[j] 60 octets => S-IMCK[j] first 40 octets, CMK[j] last 20 octets */ + RDEBUGHEX("MSK S-IMCK[j]", imck_msk.simck, sizeof(imck_msk.simck)); + RDEBUGHEX("MSK CMK[j]", imck_msk.cmk, sizeof(imck_msk.cmk)); + + if (emsklen) { + uint8_t emsk_label[20] = "TEAPbindkey@ietf.org"; + uint8_t null[1] = {0}; + uint8_t length[2] = {0,64}; /* length of 64 bytes in two bytes in network order */ + struct iovec emsk_seed[] = { + { (void *)emsk_label, sizeof(emsk_label) }, + { (void *)null, sizeof(null) }, + { (void *)length, sizeof(length) } + }; + + TLS_PRF(tls_session->ssl, + emsk, emsklen, + emsk_seed, ARRAY_SIZE(emsk_seed), + imsk_emsk, sizeof(imsk_emsk)); + + RDEBUGHEX("IMSK from EMSK", imsk_emsk, EAP_TEAP_IMSK_LEN); + + imck_seed[1].iov_base = imsk_emsk; + TLS_PRF(tls_session->ssl, + t->imck_emsk.simck, sizeof(t->imck_emsk.simck), + imck_seed, ARRAY_SIZE(imck_seed), + (uint8_t *)&imck_emsk, sizeof(imck_emsk)); + + /* IMCK[j] 60 octets => S-IMCK[j] first 40 octets, CMK[j] last 20 octets */ + RDEBUGHEX("EMSK S-IMCK[j]", imck_emsk.simck, sizeof(imck_emsk.simck)); + RDEBUGHEX("EMSK CMK[j]", imck_emsk.cmk, sizeof(imck_emsk.cmk)); + } + + memcpy(&t->imck_msk, &imck_msk, sizeof(imck_msk)); + if (emsklen) memcpy(&t->imck_emsk, &imck_emsk, sizeof(imck_emsk)); +} + +static void eap_teap_tlv_append(tls_session_t *tls_session, int tlv, bool mandatory, int length, const void *data) +{ + uint16_t hdr[2]; + + hdr[0] = htons(tlv | (mandatory ? EAP_TEAP_TLV_MANDATORY : 0)); + hdr[1] = htons(length); + + tls_session->record_plus(&tls_session->clean_in, &hdr, 4); + tls_session->record_plus(&tls_session->clean_in, data, length); +} + +static void eap_teap_send_error(tls_session_t *tls_session, int error) +{ + uint32_t value; + value = htonl(error); + + eap_teap_tlv_append(tls_session, EAP_TEAP_TLV_ERROR, true, sizeof(value), &value); +} + +static void eap_teap_append_identity(tls_session_t *tls_session, int value) { + uint16_t identity; + identity = htons(value); + + eap_teap_tlv_append(tls_session, EAP_TEAP_TLV_IDENTITY, false, sizeof(identity), &identity); +} + +static void eap_teap_append_result(tls_session_t *tls_session, PW_CODE code) +{ + teap_tunnel_t *t = (teap_tunnel_t *) tls_session->opaque; + + int type = (t->result_final) + ? EAP_TEAP_TLV_RESULT + : EAP_TEAP_TLV_INTERMED_RESULT; + + uint16_t state = (code == PW_CODE_ACCESS_REJECT) + ? EAP_TEAP_TLV_RESULT_FAILURE + : EAP_TEAP_TLV_RESULT_SUCCESS; + state = htons(state); + + eap_teap_tlv_append(tls_session, type, true, sizeof(state), &state); +} + +static void eap_teap_append_eap_identity_request(REQUEST *request, tls_session_t *tls_session, eap_handler_t *eap_session) +{ + eap_packet_raw_t eap_packet; + + RDEBUG("Sending EAP-Identity"); + + eap_packet.code = PW_EAP_REQUEST; + eap_packet.id = eap_session->eap_ds->response->id + 1; + eap_packet.length[0] = 0; + eap_packet.length[1] = EAP_HEADER_LEN + 1; + eap_packet.data[0] = PW_EAP_IDENTITY; + + eap_teap_tlv_append(tls_session, EAP_TEAP_TLV_EAP_PAYLOAD, true, sizeof(eap_packet), &eap_packet); +} + +#if 0 +static void eap_teap_send_pac_tunnel(REQUEST *request, tls_session_t *tls_session) +{ + teap_tunnel_t *t = tls_session->opaque; + eap_teap_pac_t pac; + eap_teap_attr_pac_opaque_plaintext_t opaque_plaintext; + int alen, dlen; + + memset(&pac, 0, sizeof(pac)); + memset(&opaque_plaintext, 0, sizeof(opaque_plaintext)); + + RDEBUG("Sending Tunnel PAC"); + + pac.key.hdr.type = htons(EAP_TEAP_TLV_MANDATORY | PAC_INFO_PAC_KEY); + pac.key.hdr.length = htons(sizeof(pac.key.data)); + rad_assert(sizeof(pac.key.data) % sizeof(uint32_t) == 0); + RANDFILL(pac.key.data); + + pac.info.lifetime.hdr.type = htons(PAC_INFO_PAC_LIFETIME); + pac.info.lifetime.hdr.length = htons(sizeof(pac.info.lifetime.data)); + pac.info.lifetime.data = htonl(time(NULL) + t->pac_lifetime); + + pac.info.a_id.hdr.type = htons(EAP_TEAP_TLV_MANDATORY | PAC_INFO_A_ID); + pac.info.a_id.hdr.length = htons(sizeof(pac.info.a_id.data)); + memcpy(pac.info.a_id.data, t->a_id, sizeof(pac.info.a_id.data)); + + pac.info.a_id_info.hdr.type = htons(PAC_INFO_A_ID_INFO); + pac.info.a_id_info.hdr.length = htons(sizeof(pac.info.a_id_info.data)); + #define MIN(a,b) (((a)>(b)) ? (b) : (a)) + alen = MIN(talloc_array_length(t->authority_identity) - 1, sizeof(pac.info.a_id_info.data)); + memcpy(pac.info.a_id_info.data, t->authority_identity, alen); + + pac.info.type.hdr.type = htons(EAP_TEAP_TLV_MANDATORY | PAC_INFO_PAC_TYPE); + pac.info.type.hdr.length = htons(sizeof(pac.info.type.data)); + pac.info.type.data = htons(PAC_TYPE_TUNNEL); + + pac.info.hdr.type = htons(EAP_TEAP_TLV_MANDATORY | PAC_INFO_PAC_INFO); + pac.info.hdr.length = htons(sizeof(pac.info.lifetime) + + sizeof(pac.info.a_id) + + sizeof(pac.info.a_id_info) + + sizeof(pac.info.type)); + + memcpy(&opaque_plaintext.type, &pac.info.type, sizeof(opaque_plaintext.type)); + memcpy(&opaque_plaintext.lifetime, &pac.info.lifetime, sizeof(opaque_plaintext.lifetime)); + memcpy(&opaque_plaintext.key, &pac.key, sizeof(opaque_plaintext.key)); + + + rad_assert(PAC_A_ID_LENGTH <= EVP_GCM_TLS_TAG_LEN); + memcpy(pac.opaque.aad, t->a_id, PAC_A_ID_LENGTH); + rad_assert(RAND_bytes(pac.opaque.iv, sizeof(pac.opaque.iv)) != 0); + dlen = eap_teap_encrypt((unsigned const char *)&opaque_plaintext, sizeof(opaque_plaintext), + t->a_id, PAC_A_ID_LENGTH, t->pac_opaque_key, pac.opaque.iv, + pac.opaque.data, pac.opaque.tag); + if (dlen < 0) return; + + pac.opaque.hdr.type = htons(EAP_TEAP_TLV_MANDATORY | PAC_INFO_PAC_OPAQUE); + pac.opaque.hdr.length = htons(sizeof(pac.opaque) - sizeof(pac.opaque.hdr) - sizeof(pac.opaque.data) + dlen); + + eap_teap_tlv_append(tls_session, EAP_TEAP_TLV_MANDATORY | EAP_TEAP_TLV_PAC, true, + sizeof(pac) - sizeof(pac.opaque.data) + dlen, &pac); +} +#endif + +/* + * RFC7170 and the consequences of EID5768, EID5770 and EID5775 makes the path forward unclear, + * so just do what hostapd does...which the IETF probably agree with anyway: + * https://mailarchive.ietf.org/arch/msg/emu/mXzpSGEn86Zx_fa4f1uULYMhMoM/ + */ +static void eap_teap_append_crypto_binding(REQUEST *request, tls_session_t *tls_session, + uint8_t *msk, size_t msklen, + uint8_t *emsk, size_t emsklen) +{ + teap_tunnel_t *t = tls_session->opaque; + uint8_t mac_msk[EVP_MAX_MD_SIZE], mac_emsk[EVP_MAX_MD_SIZE]; + unsigned int maclen = EVP_MAX_MD_SIZE; + uint8_t *buf; + unsigned int olen; + struct crypto_binding_buffer *cbb; + + RDEBUG("Sending Cryptobinding"); + + eap_teap_derive_imck(request, tls_session, msk, msklen, emsk, emsklen); + + t->imck_emsk_available = emsklen > 0; + + olen = tls_session->outer_tlvs_octets ? talloc_array_length(tls_session->outer_tlvs_octets) : 0; + + buf = talloc_zero_array(request, uint8_t, sizeof(struct crypto_binding_buffer) - 1/*outer_tlvs*/ + olen); + rad_assert(buf != NULL); + + cbb = (struct crypto_binding_buffer *)buf; + + CRYPTO_BINDING_BUFFER_INIT(cbb); + cbb->binding.version = EAP_TEAP_VERSION; + cbb->binding.received_version = t->received_version; + + cbb->binding.subtype = ((emsklen ? EAP_TEAP_TLV_CRYPTO_BINDING_FLAGS_CMAC_BOTH : EAP_TEAP_TLV_CRYPTO_BINDING_FLAGS_CMAC_MSK) << 4) | EAP_TEAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST; + + rad_assert(sizeof(cbb->binding.nonce) % sizeof(uint32_t) == 0); + RANDFILL(cbb->binding.nonce); + cbb->binding.nonce[sizeof(cbb->binding.nonce) - 1] &= ~0x01; /* RFC 7170, Section 4.2.13 */ + + if (olen) memcpy(cbb->outer_tlvs, tls_session->outer_tlvs_octets, olen); + + RDEBUGHEX("BUFFER for Compound MAC calculation", buf, talloc_array_length(buf)); + + const EVP_MD *md = SSL_CIPHER_get_handshake_digest(SSL_get_current_cipher(tls_session->ssl)); + HMAC(md, &t->imck_msk.cmk, EAP_TEAP_CMK_LEN, buf, talloc_array_length(buf), mac_msk, &maclen); + if (t->imck_emsk_available) { + HMAC(md, &t->imck_emsk.cmk, EAP_TEAP_CMK_LEN, buf, talloc_array_length(buf), mac_emsk, &maclen); + } + memcpy(cbb->binding.msk_compound_mac, &mac_msk, sizeof(cbb->binding.msk_compound_mac)); + if (t->imck_emsk_available) { + memcpy(cbb->binding.emsk_compound_mac, &mac_emsk, sizeof(cbb->binding.emsk_compound_mac)); + } + + eap_teap_tlv_append(tls_session, EAP_TEAP_TLV_CRYPTO_BINDING, true, sizeof(cbb->binding), (uint8_t *)&cbb->binding); +} + +static int eap_teap_verify(REQUEST *request, tls_session_t *tls_session, uint8_t const *data, unsigned int data_len) +{ + uint16_t attr; + uint16_t length; + unsigned int remaining = data_len; + int total = 0; + int num[EAP_TEAP_TLV_MAX] = {0}; + teap_tunnel_t *t = (teap_tunnel_t *) tls_session->opaque; + uint32_t present = 0; + uint32_t error = 0; + uint16_t status = 0; + + rad_assert(sizeof(present) * 8 > EAP_TEAP_TLV_MAX); + + while (remaining > 0) { + if (remaining < 4) { + RDEBUG2("EAP-TEAP TLV is too small (%u) to contain a EAP-TEAP TLV header", remaining); + return 0; + } + + memcpy(&attr, data, sizeof(attr)); + attr = ntohs(attr) & EAP_TEAP_TLV_TYPE; + + switch (attr) { + case EAP_TEAP_TLV_RESULT: + case EAP_TEAP_TLV_NAK: + case EAP_TEAP_TLV_ERROR: + case EAP_TEAP_TLV_VENDOR_SPECIFIC: + case EAP_TEAP_TLV_EAP_PAYLOAD: + case EAP_TEAP_TLV_INTERMED_RESULT: + case EAP_TEAP_TLV_PAC: + case EAP_TEAP_TLV_CRYPTO_BINDING: + num[attr]++; + present |= 1 << attr; + + if (num[EAP_TEAP_TLV_EAP_PAYLOAD] > 1) { + RDEBUG("Too many EAP-Payload TLVs"); +unexpected: + for (int i = 0; i < EAP_TEAP_TLV_MAX; i++) + if (present & (1 << i)) + RDEBUG(" - attribute %d is present", i); + eap_teap_send_error(tls_session, EAP_TEAP_ERR_UNEXPECTED_TLV); + return 0; + } + + if (num[EAP_TEAP_TLV_INTERMED_RESULT] > 1) { + RDEBUG("Too many Intermediate-Result TLVs"); + goto unexpected; + } + break; + default: + if ((data[0] & 0x80) != 0) { + RDEBUG("Unknown mandatory TLV %02x", attr); + goto unexpected; + } + + num[0]++; + } + + total++; + + memcpy(&length, data + 2, sizeof(length)); + length = ntohs(length); + + data += 4; + remaining -= 4; + + if (length > remaining) { + RDEBUG2("EAP-TEAP TLV %u is longer than room remaining in the packet (%u > %u).", attr, + length, remaining); + return 0; + } + + /* + * If the rest of the TLVs are larger than + * this attribute, continue. + * + * Otherwise, if the attribute over-flows the end + * of the TLCs, die. + */ + if (remaining < length) { + RDEBUG2("EAP-TEAP TLV overflows packet!"); + return 0; + } + + if (attr == EAP_TEAP_TLV_ERROR) { + if (length != 4) goto fail_length; + error = (data[0] << 24) | (data[1] << 16) | (data[2] << 8) | data[3]; + } + + /* + * If there's an error, we bail out of the + * authentication process before allocating + * memory. + */ + if ((attr == EAP_TEAP_TLV_INTERMED_RESULT) || (attr == EAP_TEAP_TLV_RESULT)) { + if (length < 2) { + fail_length: + RDEBUG("EAP-TEAP TLV %u is too short. Expected 2, got %d.", attr, length); + return 0; + } + + status = (data[0] << 8) | data[1]; + if (status == 0) goto unknown_value; + } + + /* + * remaining > length, continue. + */ + remaining -= length; + data += length; + } + + /* + * Check status if we have it. + */ + if (status) { + if (status == EAP_TEAP_TLV_RESULT_FAILURE) { + if (!error) { + RDEBUG("EAP-TEAP TLV Status indicates failure with error %u. Rejecting request.", error); + } else { + RDEBUG("EAP-TEAP TLV Status indicates failure. Rejecting request."); + } + return 0; + } + + if (status != EAP_TEAP_TLV_RESULT_SUCCESS) { + unknown_value: + RDEBUG("EAP-TEAP TLV Status contains unknown value %u. Rejecting request.", status); + goto unexpected; + } + } + + /* + * Check if the peer mixed & matched TLVs. + */ + if ((num[EAP_TEAP_TLV_NAK] > 0) && (num[EAP_TEAP_TLV_NAK] != total)) { + RDEBUG("NAK TLV sent with non-NAK TLVs. Rejecting request."); + goto unexpected; + } + + /* + * RFC7170 EID5844 says we can have Intermediate-Result and Result TLVs all in one + */ + + /* + * Check mandatory or not mandatory TLVs. + */ + switch (t->stage) { + case TLS_SESSION_HANDSHAKE: + if (present) { + RDEBUG("Unexpected TLVs in TLS Session Handshake stage"); + goto unexpected; + } + break; + case AUTHENTICATION: + if (present & ~((1 << EAP_TEAP_TLV_EAP_PAYLOAD) | (1 << EAP_TEAP_TLV_CRYPTO_BINDING) | (1 << EAP_TEAP_TLV_INTERMED_RESULT) | (1 << EAP_TEAP_TLV_RESULT))) { + RDEBUG("Unexpected TLVs in authentication stage"); + goto unexpected; + } + break; + case PROVISIONING: + if (present & ~((1 << EAP_TEAP_TLV_PAC) | (1 << EAP_TEAP_TLV_RESULT))) { + RDEBUG("Unexpected TLVs in provisioning stage"); + goto unexpected; + } + break; + case COMPLETE: + if (present) { + RDEBUG("Unexpected TLVs in complete stage"); + goto unexpected; + } + break; + default: + RDEBUG("Unexpected stage %d", t->stage); + return 0; + } + + /* + * We got this far. It looks OK. + */ + return 1; +} + +static ssize_t eap_teap_decode_vp(TALLOC_CTX *request, DICT_ATTR const *parent, + uint8_t const *data, size_t const attr_len, VALUE_PAIR **out) +{ + int8_t tag = TAG_NONE; + VALUE_PAIR *vp; + uint8_t const *p = data; + + /* + * FIXME: Attrlen can be larger than 253 for extended attrs! + */ + if (!parent || !out ) { + RERROR("eap_teap_decode_vp: Invalid arguments"); + return -1; + } + + /* + * Silently ignore zero-length attributes. + */ + if (attr_len == 0) return 0; + + /* + * And now that we've verified the basic type + * information, decode the actual p. + */ + vp = fr_pair_afrom_da(request, parent); + if (!vp) return -1; + + vp->vp_length = attr_len; + vp->tag = tag; + + switch (parent->type) { + case PW_TYPE_STRING: + fr_pair_value_bstrncpy(vp, p, attr_len); + break; + + case PW_TYPE_OCTETS: + fr_pair_value_memcpy(vp, p, attr_len); + break; + + case PW_TYPE_ABINARY: + if (vp->vp_length > sizeof(vp->vp_filter)) { + vp->vp_length = sizeof(vp->vp_filter); + } + memcpy(vp->vp_filter, p, vp->vp_length); + break; + + case PW_TYPE_BYTE: + vp->vp_byte = p[0]; + break; + + case PW_TYPE_SHORT: + vp->vp_short = (p[0] << 8) | p[1]; + break; + + case PW_TYPE_INTEGER: + case PW_TYPE_SIGNED: /* overloaded with vp_integer */ + memcpy(&vp->vp_integer, p, 4); + vp->vp_integer = ntohl(vp->vp_integer); + break; + + case PW_TYPE_INTEGER64: + memcpy(&vp->vp_integer64, p, 8); + vp->vp_integer64 = ntohll(vp->vp_integer64); + break; + + case PW_TYPE_DATE: + memcpy(&vp->vp_date, p, 4); + vp->vp_date = ntohl(vp->vp_date); + break; + + case PW_TYPE_ETHERNET: + memcpy(vp->vp_ether, p, 6); + break; + + case PW_TYPE_IPV4_ADDR: + memcpy(&vp->vp_ipaddr, p, 4); + break; + + case PW_TYPE_IFID: + memcpy(vp->vp_ifid, p, 8); + break; + + case PW_TYPE_IPV6_ADDR: + memcpy(&vp->vp_ipv6addr, p, 16); + break; + + case PW_TYPE_IPV6_PREFIX: + /* + * FIXME: double-check that + * (vp->vp_octets[1] >> 3) matches vp->vp_length + 2 + */ + memcpy(vp->vp_ipv6prefix, p, vp->vp_length); + if (vp->vp_length < 18) { + memset(((uint8_t *)vp->vp_ipv6prefix) + vp->vp_length, 0, + 18 - vp->vp_length); + } + break; + + case PW_TYPE_IPV4_PREFIX: + /* FIXME: do the same double-check as for IPv6Prefix */ + memcpy(vp->vp_ipv4prefix, p, vp->vp_length); + + /* + * /32 means "keep all bits". Otherwise, mask + * them out. + */ + if ((p[1] & 0x3f) > 32) { + uint32_t addr, mask; + + memcpy(&addr, vp->vp_octets + 2, sizeof(addr)); + mask = 1; + mask <<= (32 - (p[1] & 0x3f)); + mask--; + mask = ~mask; + mask = htonl(mask); + addr &= mask; + memcpy(vp->vp_ipv4prefix + 2, &addr, sizeof(addr)); + } + break; + + default: + RERROR("eap_teap_decode_vp: type %d Internal sanity check %d ", parent->type, __LINE__); + fr_pair_list_free(&vp); + return -1; + } + vp->type = VT_DATA; + *out = vp; + return attr_len; +} + + +VALUE_PAIR *eap_teap_teap2vp(REQUEST *request, SSL *ssl, uint8_t const *data, size_t data_len, + DICT_ATTR const *teap_da, vp_cursor_t *out) +{ + uint16_t attr; + uint16_t length; + size_t data_left = data_len; + VALUE_PAIR *first = NULL; + VALUE_PAIR *vp = NULL; + DICT_ATTR const *da; + + if (!teap_da) + teap_da = dict_attrbyvalue(PW_FREERADIUS_EAP_TEAP_TLV, VENDORPEC_FREERADIUS); + rad_assert(teap_da != NULL); + + if (!out) { + out = talloc(request, vp_cursor_t); + rad_assert(out != NULL); + fr_cursor_init(out, &first); + } + + /* + * Decode the TLVs + */ + while (data_left > 0) { + ssize_t decoded; + + /* FIXME do something with mandatory */ + + memcpy(&attr, data, sizeof(attr)); + attr = ntohs(attr) & EAP_TEAP_TLV_TYPE; + + memcpy(&length, data + 2, sizeof(length)); + length = ntohs(length); + + data += 4; + data_left -= 4; + + /* + * Look up the TLV. + * + * For now, if it doesn't exist, ignore it. + */ + da = dict_attrbyparent(teap_da, attr, teap_da->vendor); + if (!da) { + RDEBUG("eap_teap_teap2vp: no sub attribute found %s attr: %u vendor: %u", + teap_da->name, attr, teap_da->vendor); + goto next_attr; + } + if (da->type == PW_TYPE_TLV) { + eap_teap_teap2vp(request, ssl, data, length, da, out); + goto next_attr; + } + decoded = eap_teap_decode_vp(request, da, data, length, &vp); + if (decoded < 0) { + RERROR("Failed decoding %s: %s", da->name, fr_strerror()); + goto next_attr; + } + + fr_cursor_merge(out, vp); + + next_attr: + while (fr_cursor_next(out)) { + /* nothing */ + } + + data += length; + data_left -= length; + } + + /* + * We got this far. It looks OK. + */ + return first; +} + + +static void eapteap_copy_request_to_tunnel(REQUEST *request, REQUEST *fake) { + VALUE_PAIR *copy, *vp; + vp_cursor_t cursor; + + for (vp = fr_cursor_init(&cursor, &request->packet->vps); + vp; + vp = fr_cursor_next(&cursor)) { + /* + * The attribute is a server-side thingy, + * don't copy it. + */ + if ((vp->da->attr > 255) && (((vp->da->attr >> 16) & 0xffff) == 0)) { + continue; + } + + /* + * The outside attribute is already in the + * tunnel, don't copy it. + * + * This works for BOTH attributes which + * are originally in the tunneled request, + * AND attributes which are copied there + * from below. + */ + if (fr_pair_find_by_da(fake->packet->vps, vp->da, TAG_ANY)) continue; + + /* + * Some attributes are handled specially. + */ + if (!vp->da->vendor) switch (vp->da->attr) { + /* + * NEVER copy Message-Authenticator, + * EAP-Message, or State. They're + * only for outside of the tunnel. + */ + case PW_USER_NAME: + case PW_USER_PASSWORD: + case PW_CHAP_PASSWORD: + case PW_CHAP_CHALLENGE: + case PW_PROXY_STATE: + case PW_MESSAGE_AUTHENTICATOR: + case PW_EAP_MESSAGE: + case PW_STATE: + continue; + + /* + * By default, copy it over. + */ + default: + break; + } + + /* + * Don't copy from the head, we've already + * checked it. + */ + copy = fr_pair_list_copy_by_num(fake->packet, vp, vp->da->attr, vp->da->vendor, TAG_ANY); + fr_pair_add(&fake->packet->vps, copy); + } +} + +/* + * Use a reply packet to determine what to do. + */ +static rlm_rcode_t CC_HINT(nonnull) process_reply(eap_handler_t *eap_session, + tls_session_t *tls_session, + REQUEST *request, RADIUS_PACKET *reply) +{ + rlm_rcode_t rcode = RLM_MODULE_REJECT; + VALUE_PAIR *vp; + vp_cursor_t cursor; + uint8_t msk[2 * CHAP_VALUE_LENGTH] = {0}, emsk[2 * EAPTLS_MPPE_KEY_LEN] = {0}; + size_t msklen = 0, emsklen = 0; + + teap_tunnel_t *t = tls_session->opaque; + + rad_assert(eap_session->request == request); + + /* + * If the response packet was Access-Accept, then + * we're OK. If not, die horribly. + * + * FIXME: EAP-Messages can only start with 'identity', + * NOT 'eap start', so we should check for that.... + */ + switch (reply->code) { + case PW_CODE_ACCESS_ACCEPT: + RDEBUG("Got tunneled Access-Accept"); + + for (vp = fr_cursor_init(&cursor, &reply->vps); vp; vp = fr_cursor_next(&cursor)) { + if (vp->da->attr == PW_EAP_EMSK) { + // FIXME check if we should be generating an emsk from MPPE keys below + emsklen = MIN(vp->vp_length, sizeof(emsk)); + memcpy(emsk, vp->vp_octets, emsklen); + break; + } + + if (vp->da->vendor != VENDORPEC_MICROSOFT) continue; + + /* like for EAP-FAST, the keying material is used reversed */ + switch (vp->da->attr) { + case PW_MSCHAP_MPPE_SEND_KEY: + if (vp->vp_length == EAPTLS_MPPE_KEY_LEN) { + /* do not set emsklen here so not to blat EAP-EMSK */ + // emsklen = sizeof(emsk); + memcpy(emsk, vp->vp_octets, EAPTLS_MPPE_KEY_LEN); + } else if (vp->vp_length == CHAP_VALUE_LENGTH) { + msklen = sizeof(msk); + memcpy(msk, vp->vp_octets, CHAP_VALUE_LENGTH); + } else { + wrong_length: + REDEBUG("Found %s with incorrect length. Expected %u or %u, got %zu", + vp->da->name, CHAP_VALUE_LENGTH, EAPTLS_MPPE_KEY_LEN, vp->vp_length); + return RLM_MODULE_INVALID; + } + + RDEBUGHEX("MSCHAP_MPPE_SEND_KEY [low MSK]", vp->vp_octets, vp->length); + break; + + case PW_MSCHAP_MPPE_RECV_KEY: + /* only do this if there is no EAP-EMSK */ + if (vp->vp_length == EAPTLS_MPPE_KEY_LEN && emsklen == 0) { + msklen = sizeof(msk); + memcpy(msk, vp->vp_octets, EAPTLS_MPPE_KEY_LEN); + emsklen = sizeof(emsk); + memcpy(&emsk[EAPTLS_MPPE_KEY_LEN], vp->vp_octets, EAPTLS_MPPE_KEY_LEN); + } else if (vp->vp_length == CHAP_VALUE_LENGTH) { + msklen = sizeof(msk); + memcpy(&msk[CHAP_VALUE_LENGTH], vp->vp_octets, CHAP_VALUE_LENGTH); + } else { + goto wrong_length; + } + + RDEBUGHEX("MSCHAP_MPPE_RECV_KEY [high MSK]", vp->vp_octets, vp->vp_length); + break; + + case PW_MSCHAP2_SUCCESS: + RDEBUG("Got %s, tunneling it to the client in a challenge", vp->da->name); + if (t->use_tunneled_reply) { + t->authenticated = true; + /* + * Clean up the tunneled reply. + */ + fr_pair_delete_by_num(&reply->vps, PW_PROXY_STATE, 0, TAG_ANY); + fr_pair_delete_by_num(&reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY); + fr_pair_delete_by_num(&reply->vps, PW_MESSAGE_AUTHENTICATOR, 0, TAG_ANY); + + /* + * Delete MPPE keys & encryption policy. We don't + * want these here. + */ + fr_pair_delete_by_num(&reply->vps, 7, VENDORPEC_MICROSOFT, TAG_ANY); + fr_pair_delete_by_num(&reply->vps, 8, VENDORPEC_MICROSOFT, TAG_ANY); + fr_pair_delete_by_num(&reply->vps, 16, VENDORPEC_MICROSOFT, TAG_ANY); + fr_pair_delete_by_num(&reply->vps, 17, VENDORPEC_MICROSOFT, TAG_ANY); + + fr_pair_list_free(&t->accept_vps); /* for proxying MS-CHAP2 */ + fr_pair_list_mcopy_by_num(t, &t->accept_vps, &reply->vps, 0, 0, TAG_ANY); + rad_assert(!reply->vps); + } + break; + + default: + break; + } + } + + if (t->use_tunneled_reply) { + /* + * Clean up the tunneled reply. + */ + fr_pair_delete_by_num(&reply->vps, PW_EAP_EMSK, 0, TAG_ANY); + fr_pair_delete_by_num(&reply->vps, PW_EAP_SESSION_ID, 0, TAG_ANY); + } + + eap_teap_append_result(tls_session, reply->code); + eap_teap_append_crypto_binding(request, tls_session, msk, msklen, emsk, emsklen); + + vp = fr_pair_find_by_num(request->state, PW_EAP_TEAP_TLV_IDENTITY, VENDORPEC_FREERADIUS, TAG_ANY); + if (vp) { + RDEBUG("&session-state:FreeRADIUS-EAP-TEAP-TLV-Identity-Type set so continuing EAP sequence/chaining"); + + /* RFC3748, Section 2.1 - does not explictly tell us to but we need to eat the EAP-Success */ + fr_pair_delete_by_num(&reply->vps, PW_EAP_MESSAGE, 0, TAG_ANY); + + /* new identity */ + talloc_free(t->username); + t->username = NULL; + + /* RFC7170, Appendix C.6 */ + eap_teap_append_identity(tls_session, vp->vp_short); + eap_teap_append_eap_identity_request(request, tls_session, eap_session); + + goto challenge; + } + + t->result_final = true; + eap_teap_append_result(tls_session, reply->code); + + tls_session->authentication_success = true; + rcode = RLM_MODULE_OK; + + break; + + case PW_CODE_ACCESS_REJECT: + RDEBUG("Got tunneled Access-Reject"); + + eap_teap_append_result(tls_session, reply->code); + rcode = RLM_MODULE_REJECT; + break; + + /* + * Handle Access-Challenge, but only if we + * send tunneled reply data. This is because + * an Access-Challenge means that we MUST tunnel + * a Reply-Message to the client. + */ + case PW_CODE_ACCESS_CHALLENGE: + RDEBUG("Got tunneled Access-Challenge"); +challenge: + /* + * Keep the State attribute, if necessary. + * + * Get rid of the old State, too. + */ + fr_pair_list_free(&t->state); + fr_pair_list_mcopy_by_num(t, &t->state, &reply->vps, PW_STATE, 0, TAG_ANY); + + /* + * Copy the EAP-Message back to the tunnel. + */ + (void) fr_cursor_init(&cursor, &reply->vps); + + while ((vp = fr_cursor_next_by_num(&cursor, PW_EAP_MESSAGE, 0, TAG_ANY)) != NULL) { + eap_teap_tlv_append(tls_session, EAP_TEAP_TLV_EAP_PAYLOAD, true, vp->vp_length, vp->vp_octets); + } + + /* + * When chaining, we 'goto challenge' and can use that to now signal back + * to unlang that a method has completed and we can now move to the next + */ + rcode = reply->code == PW_CODE_ACCESS_CHALLENGE ? RLM_MODULE_HANDLED : RLM_MODULE_OK; + break; + + default: + RDEBUG("Unknown RADIUS packet type %d: rejecting tunneled user", reply->code); + rcode = RLM_MODULE_INVALID; + break; + } + + + return rcode; +} + +static PW_CODE eap_teap_eap_payload(REQUEST *request, eap_handler_t *eap_session, + tls_session_t *tls_session, + VALUE_PAIR *vp_eap, VALUE_PAIR *vp_type) +{ + PW_CODE code = PW_CODE_ACCESS_REJECT; + rlm_rcode_t rcode; + VALUE_PAIR *vp; + teap_tunnel_t *t; + REQUEST *fake; + + RDEBUG("Processing received EAP Payload"); + + /* + * Allocate a fake REQUEST structure. + */ + fake = request_alloc_fake(request); + rad_assert(!fake->packet->vps); + + t = (teap_tunnel_t *) tls_session->opaque; + + /* + * Add the tunneled attributes to the fake request. + */ + + fake->packet->vps = fr_pair_afrom_num(fake->packet, PW_EAP_MESSAGE, 0); + fr_pair_value_memcpy(fake->packet->vps, vp_eap->vp_octets, vp_eap->vp_length); + + if (vp_type) fr_pair_add(&fake->packet->vps, fr_pair_copy(fake->packet, vp_type)); + + RDEBUG("Got tunneled request"); + rdebug_pair_list(L_DBG_LVL_1, request, fake->packet->vps, NULL); + + /* + * Tell the request that it's a fake one. + */ + fr_pair_make(fake->packet, &fake->packet->vps, "Freeradius-Proxied-To", "127.0.0.1", T_OP_EQ); + + /* + * No User-Name in the stored data, look for + * an EAP-Identity, and pull it out of there. + */ + if (!t->username) { + vp = fr_pair_find_by_num(fake->packet->vps, PW_EAP_MESSAGE, 0, TAG_ANY); + if (vp && + (vp->vp_length >= EAP_HEADER_LEN + 2) && + (vp->vp_strvalue[0] == PW_EAP_RESPONSE) && + (vp->vp_strvalue[EAP_HEADER_LEN] == PW_EAP_IDENTITY) && + (vp->vp_strvalue[EAP_HEADER_LEN + 1] != 0)) { + /* + * Create & remember a User-Name + */ + t->username = fr_pair_make(t, NULL, "User-Name", NULL, T_OP_EQ); + rad_assert(t->username != NULL); + + fr_pair_value_bstrncpy(t->username, vp->vp_octets + 5, vp->vp_length - 5); + + RDEBUG("Got tunneled identity of %s", t->username->vp_strvalue); + } else { + /* + * Don't reject the request outright, + * as it's permitted to do EAP without + * user-name. + */ + RWDEBUG2("No EAP-Identity found to start EAP conversation"); + } + } /* else there WAS a t->username */ + + if (t->username) { + vp = fr_pair_list_copy(fake->packet, t->username); + fr_pair_add(&fake->packet->vps, vp); + fake->username = vp; + } + + /* + * Add the State attribute, too, if it exists. + */ + if (t->state) { + vp = fr_pair_list_copy(fake->packet, t->state); + if (vp) fr_pair_add(&fake->packet->vps, vp); + } + + if (t->stage == AUTHENTICATION) { + VALUE_PAIR *tvp; + + RDEBUG2("AUTHENTICATION"); + + if (t->default_method) { + /* + * RFC 7170 - Authenticating Using EAP-TEAP-MSCHAPv2 + */ + if (t->default_method == PW_EAP_MSCHAPV2 && t->mode == EAP_TEAP_PROVISIONING_ANON) { + tvp = fr_pair_afrom_num(fake, PW_MSCHAP_CHALLENGE, VENDORPEC_MICROSOFT); + //fr_pair_value_memcpy(tvp, t->keyblock->server_challenge, CHAP_VALUE_LENGTH); + fr_pair_add(&fake->config, tvp); + + tvp = fr_pair_afrom_num(fake, PW_MS_CHAP_PEER_CHALLENGE, 0); + //fr_pair_value_memcpy(tvp, t->keyblock->client_challenge, CHAP_VALUE_LENGTH); + fr_pair_add(&fake->config, tvp); + } + } + } + + if (t->copy_request_to_tunnel) { + eapteap_copy_request_to_tunnel(request, fake); + } + + if ((vp = fr_pair_find_by_num(request->config, PW_VIRTUAL_SERVER, 0, TAG_ANY)) != NULL) { + fake->server = vp->vp_strvalue; + + } else if (t->virtual_server) { + fake->server = t->virtual_server; + + } /* else fake->server == request->server */ + + /* + * Call authentication recursively, which will + * do PAP, CHAP, MS-CHAP, etc. + */ + rad_virtual_server(fake); + + /* + * Decide what to do with the reply. + */ + switch (fake->reply->code) { + case 0: + RDEBUG("No tunneled reply was found, rejecting the user."); + code = PW_CODE_ACCESS_REJECT; + break; + + default: + /* + * Returns RLM_MODULE_FOO, and we want to return PW_FOO + */ + rcode = process_reply(eap_session, tls_session, request, fake->reply); + switch (rcode) { + case RLM_MODULE_REJECT: + code = PW_CODE_ACCESS_REJECT; + break; + + case RLM_MODULE_HANDLED: + code = PW_CODE_ACCESS_CHALLENGE; + break; + + case RLM_MODULE_OK: + code = PW_CODE_ACCESS_ACCEPT; + break; + + default: + code = PW_CODE_ACCESS_REJECT; + break; + } + break; + } + + talloc_free(fake); + + return code; +} + +static PW_CODE eap_teap_crypto_binding(REQUEST *request, UNUSED eap_handler_t *eap_session, + tls_session_t *tls_session, eap_tlv_crypto_binding_tlv_t const *binding) +{ + teap_tunnel_t *t = tls_session->opaque; + uint8_t *buf; + unsigned int olen; + struct crypto_binding_buffer *cbb; + uint8_t mac[EVP_MAX_MD_SIZE]; + unsigned int maclen = sizeof(mac); + unsigned int flags; + struct teap_imck_t *imck = NULL; + + olen = tls_session->outer_tlvs_octets ? talloc_array_length(tls_session->outer_tlvs_octets) : 0; + /* FIXME: include client outer TLVs */ + + buf = talloc_zero_array(request, uint8_t, sizeof(struct crypto_binding_buffer) - 1/*outer_tlvs*/ + olen); + rad_assert(buf != NULL); + + cbb = (struct crypto_binding_buffer *)buf; + + if (binding->version != t->received_version || binding->received_version != EAP_TEAP_VERSION) { + RDEBUG2("Crypto-Binding TLV version mis-match (possible downgrade attack!)"); + return PW_CODE_ACCESS_REJECT; + } + if ((binding->subtype & 0xf) != EAP_TEAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE) { + RDEBUG2("Crypto-Binding TLV unexpected non-response"); + return PW_CODE_ACCESS_REJECT; + } + flags = binding->subtype >> 4; + + CRYPTO_BINDING_BUFFER_INIT(cbb); + memcpy(&cbb->binding, binding, sizeof(cbb->binding) - sizeof(cbb->binding.emsk_compound_mac) - sizeof(cbb->binding.msk_compound_mac)); + if (olen) memcpy(cbb->outer_tlvs, tls_session->outer_tlvs_octets, olen); + + RDEBUGHEX("BUFFER for Compound MAC calculation", buf, talloc_array_length(buf)); + + /* + * we carry forward the S-IMCK[j] based on what we verified for session key generation + * + * https://mailarchive.ietf.org/arch/msg/emu/mXzpSGEn86Zx_fa4f1uULYMhMoM/ + * https://github.com/emu-wg/teap-errata/pull/13 + */ + const EVP_MD *md = SSL_CIPHER_get_handshake_digest(SSL_get_current_cipher(tls_session->ssl)); + if (flags != EAP_TEAP_TLV_CRYPTO_BINDING_FLAGS_CMAC_EMSK) { + HMAC(md, &t->imck_msk.cmk, sizeof(t->imck_msk.cmk), buf, talloc_array_length(buf), mac, &maclen); + if (memcmp(binding->msk_compound_mac, mac, sizeof(binding->msk_compound_mac))) { + RDEBUG2("Crypto-Binding TLV (MSK) mis-match"); + return PW_CODE_ACCESS_REJECT; + } + imck = &t->imck_msk; + } + if (flags != EAP_TEAP_TLV_CRYPTO_BINDING_FLAGS_CMAC_MSK && t->imck_emsk_available) { + HMAC(md, &t->imck_emsk.cmk, sizeof(t->imck_emsk.cmk), buf, talloc_array_length(buf), mac, &maclen); + if (memcmp(binding->emsk_compound_mac, mac, sizeof(binding->emsk_compound_mac))) { + RDEBUG2("Crypto-Binding TLV (EMSK) mis-match"); + return PW_CODE_ACCESS_REJECT; + } + imck = &t->imck_emsk; + } + + if (!imck) imck = &imck_zeros; + + /* IMCK[j] 60 octets => S-IMCK[j] first 40 octets, CMK[j] last 20 octets */ + RDEBUGHEX("S-IMCK[j]", imck->simck, sizeof(imck->simck)); + + uint8_t mk_msk_label[31] = "Session Key Generating Function"; + + struct iovec mk_msk_seed[1] = { + { (void *)mk_msk_label, sizeof(mk_msk_label) } + }; + TLS_PRF(tls_session->ssl, + imck->simck, sizeof(imck->simck), + mk_msk_seed, ARRAY_SIZE(mk_msk_seed), + (uint8_t *)&t->msk, sizeof(t->msk)); + RDEBUGHEX("Derived key (MSK)", t->msk, sizeof(t->msk)); + + uint8_t mk_emsk_label[40] = "Extended Session Key Generating Function"; + struct iovec mk_emsk_seed[1] = { + { (void *)mk_emsk_label, sizeof(mk_emsk_label) } + }; + TLS_PRF(tls_session->ssl, + imck->simck, sizeof(imck->simck), + mk_emsk_seed, ARRAY_SIZE(mk_emsk_seed), + (uint8_t *)&t->emsk, sizeof(t->emsk)); + RDEBUGHEX("Derived key (EMSK)", t->emsk, sizeof(t->emsk)); + + return PW_CODE_ACCESS_ACCEPT; +} + + +static PW_CODE eap_teap_process_tlvs(REQUEST *request, eap_handler_t *eap_session, + tls_session_t *tls_session, VALUE_PAIR *teap_vps) +{ + teap_tunnel_t *t = (teap_tunnel_t *) tls_session->opaque; + VALUE_PAIR *vp, *vp_eap = NULL, *vp_type = NULL; + vp_cursor_t cursor; + PW_CODE code = PW_CODE_ACCESS_ACCEPT; + bool gotintermedresult = false, gotresult = false, gotcryptobinding = false; + + for (vp = fr_cursor_init(&cursor, &teap_vps); vp; vp = fr_cursor_next(&cursor)) { + char *value; + DICT_ATTR const *parent_da = NULL; + parent_da = dict_parent(vp->da->attr, vp->da->vendor); + if (parent_da == NULL || vp->da->vendor != VENDORPEC_FREERADIUS || + ((vp->da->attr & 0xff) != PW_FREERADIUS_EAP_TEAP_TLV)) { + value = vp_aprints(request->packet, vp, '"'); + RDEBUG2("ignoring non-EAP-TEAP TLV %s", value); + talloc_free(value); + continue; + } + + switch (parent_da->attr) { + case PW_FREERADIUS_EAP_TEAP_TLV: + switch (vp->da->attr >> 8) { + case EAP_TEAP_TLV_IDENTITY: + vp_type = vp; + break; + case EAP_TEAP_TLV_EAP_PAYLOAD: + vp_eap = vp; + break; + case EAP_TEAP_TLV_RESULT: + gotresult = true; + if (vp->vp_short != EAP_TEAP_TLV_RESULT_SUCCESS) code = PW_CODE_ACCESS_REJECT; + break; + case EAP_TEAP_TLV_INTERMED_RESULT: + gotintermedresult = true; + if (vp->vp_short != EAP_TEAP_TLV_RESULT_SUCCESS) code = PW_CODE_ACCESS_REJECT; + break; + case EAP_TEAP_TLV_CRYPTO_BINDING: + gotcryptobinding = true; + if (vp->vp_length >= sizeof(eap_tlv_crypto_binding_tlv_t)) + code = eap_teap_crypto_binding(request, eap_session, tls_session, + (eap_tlv_crypto_binding_tlv_t const *)vp->vp_octets); + break; + default: + value = vp_aprints_value(request->packet, vp, '"'); + RDEBUG2("ignoring unknown %s", value); + talloc_free(value); + } + break; + case PW_EAP_TEAP_TLV_PAC: + switch ( ( vp->da->attr >> 16 )) { + case PAC_INFO_PAC_ACK: + if (vp->vp_integer == EAP_TEAP_TLV_RESULT_SUCCESS) { + t->pac.expires = UINT32_MAX; + t->pac.expired = false; + } + break; + case PAC_INFO_PAC_TYPE: + if (vp->vp_integer != PAC_TYPE_TUNNEL) { + RDEBUG("only able to serve Tunnel PAC's, ignoring request"); + break; + } + t->pac.send = true; + break; + default: + value = vp_aprints(request->packet, vp, '"'); + RDEBUG2("ignoring unknown EAP-TEAP-PAC-TLV %s", value); + talloc_free(value); + } + break; + default: + value = vp_aprints(request->packet, vp, '"'); + RDEBUG2("ignoring EAP-TEAP TLV %s", value); + talloc_free(value); + } + + if (code == PW_CODE_ACCESS_REJECT) + return PW_CODE_ACCESS_REJECT; + } + + if (t->stage == AUTHENTICATION) { + if (gotcryptobinding && gotintermedresult) t->stage = PROVISIONING; + /* rollback if we have an EAP sequence (chaining) */ + vp = fr_pair_find_by_num(request->state, PW_EAP_TEAP_TLV_IDENTITY, VENDORPEC_FREERADIUS, TAG_ANY); + if (t->stage == PROVISIONING && !gotresult && vp) t->stage = AUTHENTICATION; + } + if (t->stage == PROVISIONING) { + if (gotcryptobinding && gotresult) t->stage = COMPLETE; + } + + if (vp_eap) + code = eap_teap_eap_payload(request, eap_session, tls_session, vp_eap, vp_type); + + return code; +} + + +static void print_tunneled_data(uint8_t const *data, size_t data_len) +{ + size_t i; + + DEBUG2(" TEAP tunnel data total %zu", data_len); + + if ((rad_debug_lvl > 2) && fr_log_fp) { + for (i = 0; i < data_len; i++) { + if ((i & 0x0f) == 0) fprintf(fr_log_fp, " TEAP tunnel data in %02x: ", (int) i); + + fprintf(fr_log_fp, "%02x ", data[i]); + + if ((i & 0x0f) == 0x0f) fprintf(fr_log_fp, "\n"); + } + if ((data_len & 0x0f) != 0) fprintf(fr_log_fp, "\n"); + } +} + + +/* + * Process the inner tunnel data + */ +PW_CODE eap_teap_process(eap_handler_t *eap_session, tls_session_t *tls_session) +{ + PW_CODE code; + VALUE_PAIR *teap_vps, *vp; + uint8_t const *data; + size_t data_len; + teap_tunnel_t *t; + REQUEST *request = eap_session->request; + + /* + * Just look at the buffer directly, without doing + * record_to_buff. + */ + data_len = tls_session->clean_out.used; + tls_session->clean_out.used = 0; + data = tls_session->clean_out.data; + + t = (teap_tunnel_t *) tls_session->opaque; + + if (rad_debug_lvl > 2) print_tunneled_data(data, data_len); + + /* + * See if the tunneled data is well formed. + */ + if (!eap_teap_verify(request, tls_session, data, data_len)) return PW_CODE_ACCESS_REJECT; + + if (t->stage == TLS_SESSION_HANDSHAKE) { + rad_assert(t->mode == EAP_TEAP_UNKNOWN); + + char buf[256]; + if (strstr(SSL_CIPHER_description(SSL_get_current_cipher(tls_session->ssl), + buf, sizeof(buf)), "Au=None")) { + /* FIXME enforce MSCHAPv2 - RFC 7170 */ + RDEBUG2("Using anonymous provisioning"); + t->mode = EAP_TEAP_PROVISIONING_ANON; + t->pac.send = true; + } else { + if (SSL_session_reused(tls_session->ssl)) { + RDEBUG("Session Resumed from PAC"); + t->mode = EAP_TEAP_NORMAL_AUTH; + } else { + RDEBUG2("Using authenticated provisioning"); + t->mode = EAP_TEAP_PROVISIONING_AUTH; + } + + /* + * Send a new pac at ~0.6 times the lifetime. + */ + if (!t->pac.expires || t->pac.expired || t->pac.expires < (time(NULL) + (t->pac_lifetime >> 1) + (t->pac_lifetime >> 3))) { + t->pac.send = true; + } + } + + eap_teap_init_keys(request, tls_session); + + /* RFC7170, Appendix C.6 */ + vp = fr_pair_find_by_num(request->state, PW_EAP_TEAP_TLV_IDENTITY, VENDORPEC_FREERADIUS, TAG_ANY); + if (vp) eap_teap_append_identity(tls_session, vp->vp_short); + + eap_teap_append_eap_identity_request(request, tls_session, eap_session); + + t->stage = AUTHENTICATION; + + tls_handshake_send(request, tls_session); + + return PW_CODE_ACCESS_CHALLENGE; + } + + teap_vps = eap_teap_teap2vp(request, tls_session->ssl, data, data_len, NULL, NULL); + + RDEBUG("Got Tunneled TEAP TLVs"); + rdebug_pair_list(L_DBG_LVL_1, request, teap_vps, NULL); + + code = eap_teap_process_tlvs(request, eap_session, tls_session, teap_vps); + + fr_pair_list_free(&teap_vps); + + if (code == PW_CODE_ACCESS_REJECT) return PW_CODE_ACCESS_REJECT; + + switch (t->stage) { + case AUTHENTICATION: + code = PW_CODE_ACCESS_CHALLENGE; + break; + + case PROVISIONING: + if (!t->result_final) { + t->result_final = true; + eap_teap_append_result(tls_session, code); + } + +#if 0 + if (t->pac.send) { + RDEBUG("Peer requires new PAC"); + eap_teap_send_pac_tunnel(request, tls_session); + code = PW_CODE_ACCESS_CHALLENGE; + break; + } +#endif + + /* FALL-THROUGH */ + + case COMPLETE: +#if 0 + /* + * RFC 7170 - Network Access after EAP-TEAP Provisioning + */ + if (t->pac.type && t->pac.expired) { + REDEBUG("Rejecting expired PAC."); + code = PW_CODE_ACCESS_REJECT; + break; + } + + if (t->mode == EAP_TEAP_PROVISIONING_ANON) { + REDEBUG("Rejecting unauthenticated provisioning"); + code = PW_CODE_ACCESS_REJECT; + break; + } +#endif + /* + * TEAP wants to use it's own MSK, so boo to eap_tls_gen_mppe_keys() + */ + eap_add_reply(request, "MS-MPPE-Recv-Key", t->msk, EAPTLS_MPPE_KEY_LEN); + eap_add_reply(request, "MS-MPPE-Send-Key", &t->msk[EAPTLS_MPPE_KEY_LEN], EAPTLS_MPPE_KEY_LEN); + eap_add_reply(request, "EAP-MSK", t->msk, sizeof(t->msk)); + eap_add_reply(request, "EAP-EMSK", t->emsk, sizeof(t->emsk)); + + break; + + default: + RERROR("Internal sanity check failed in EAP-TEAP at %d", t->stage); + code = PW_CODE_ACCESS_REJECT; + } + + tls_handshake_send(request, tls_session); + + return code; +} diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap.h b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap.h new file mode 100644 index 0000000..d6905ec --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap.h @@ -0,0 +1,275 @@ +/* + * eap_teap.h + * + * Version: $Id$ + * + * Copyright (C) 2022 Network RADIUS SARL <legal@networkradius.com> + * + * This software may not be redistributed in any form without the prior + * written consent of Network RADIUS. + * + * 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. + */ +#ifndef _EAP_TEAP_H +#define _EAP_TEAP_H + +RCSIDH(eap_teap_h, "$Id$") + +#include "eap_tls.h" + +#define EAP_TEAP_VERSION 1 + +#define EAP_TEAP_MSK_LEN 64 +#define EAP_TEAP_EMSK_LEN 64 +#define EAP_TEAP_IMSK_LEN 32 +#define EAP_TEAP_SKS_LEN 40 +#define EAP_TEAP_SIMCK_LEN 40 +#define EAP_TEAP_CMK_LEN 20 + +#define EAP_TEAP_TLV_MANDATORY 0x8000 +#define EAP_TEAP_TLV_TYPE 0x3fff + +#define EAP_TEAP_ERR_TUNNEL_COMPROMISED 2001 +#define EAP_TEAP_ERR_UNEXPECTED_TLV 2002 + +/* intermediate result values also match */ +#define EAP_TEAP_TLV_RESULT_SUCCESS 1 +#define EAP_TEAP_TLV_RESULT_FAILURE 2 + +typedef enum eap_teap_stage_t { + TLS_SESSION_HANDSHAKE = 0, + AUTHENTICATION, + PROVISIONING, + COMPLETE +} eap_teap_stage_t; + +typedef enum eap_teap_auth_type { + EAP_TEAP_UNKNOWN = 0, + EAP_TEAP_PROVISIONING_ANON, + EAP_TEAP_PROVISIONING_AUTH, + EAP_TEAP_NORMAL_AUTH +} eap_teap_auth_type_t; + +typedef enum eap_teap_pac_info_attr_type_t { + PAC_INFO_PAC_KEY = 1, // 1 + PAC_INFO_PAC_OPAQUE, // 2 + PAC_INFO_PAC_LIFETIME, // 3 + PAC_INFO_A_ID, // 4 + PAC_INFO_I_ID, // 5 + PAC_INFO_PAC_RESERVED6, // 6 + PAC_INFO_A_ID_INFO, // 7 + PAC_INFO_PAC_ACK, // 8 + PAC_INFO_PAC_INFO, // 9 + PAC_INFO_PAC_TYPE, // 10 + PAC_INFO_MAX +} eap_teap_pac_info_attr_type_t; + +typedef enum eap_teap_pac_type_t { + PAC_TYPE_TUNNEL = 1, // 1 + PAC_TYPE_MACHINE_AUTH, // 2 + PAC_TYPE_USER_AUTHZ, // 3 + PAC_TYPE_MAX +} eap_teap_pac_type_t; + +#define PAC_KEY_LENGTH 32 +#define PAC_A_ID_LENGTH 16 +#define PAC_I_ID_LENGTH 16 +#define PAC_A_ID_INFO_LENGTH 32 + +/* + * 11 - PAC TLV + */ +typedef struct eap_teap_pac_attr_hdr_t { + uint16_t type; + uint16_t length; +} CC_HINT(__packed__) eap_teap_pac_attr_hdr_t; + +/* + * 11.1 - Key + */ +typedef struct eap_teap_pac_attr_key_t { + eap_teap_pac_attr_hdr_t hdr; + uint8_t data[1]; +} CC_HINT(__packed__) eap_teap_pac_attr_key_t; + +/* + * 11.2 - Opaque + */ +typedef struct eap_teap_pac_attr_opaque_t { + eap_teap_pac_attr_hdr_t hdr; + uint8_t data[1]; +} CC_HINT(__packed__) eap_teap_pac_attr_opaque_t; + +/* + * 11.3 and 11.9.3 - lifetime + */ +typedef struct eap_teap_pac_attr_lifetime_t { + eap_teap_pac_attr_hdr_t hdr; + uint32_t data; // secs since epoch +} CC_HINT(__packed__) eap_teap_pac_attr_lifetime_t; + +/* + * 11.4 and 11.9.4 - A-ID + */ +typedef struct eap_teap_pac_attr_a_id_t { + eap_teap_pac_attr_hdr_t hdr; + uint8_t data[1]; +} CC_HINT(__packed__) eap_teap_pac_attr_a_id_t; + +/* + * 11.5 and 11.9.5 - I-ID + */ +typedef struct eap_teap_pac_attr_i_id_t { + eap_teap_pac_attr_hdr_t hdr; + uint8_t data[1]; +} CC_HINT(__packed__) eap_teap_pac_attr_i_id_t; + +/* + * 11.7 and 11.9.7 - A-ID-Info + */ +typedef struct eap_teap_pac_attr_a_id_info_t { + eap_teap_pac_attr_hdr_t hdr; + uint8_t data[1]; +} CC_HINT(__packed__) eap_teap_pac_attr_a_id_info_t; + +/* + * 11.8 - Acknowledgement + */ +typedef struct eap_teap_pac_pac_attr_acknowlegement_t { + eap_teap_pac_attr_hdr_t hdr; + uint16_t data; /* 1 = success, 2 = failure */ +} CC_HINT(__packed__) eap_teap_pac_pac_attr_acknowlegement_t; + +/* + * 11.9 - Info + * + * MUST contain A-ID (4), A-ID-Info (7), and PAC-Type (10). MAY contain others. + */ +typedef struct eap_teap_pac_pac_attr_info_t { + eap_teap_pac_attr_hdr_t hdr; + uint8_t data[1]; /* sub TLVs */ +} CC_HINT(__packed__) eap_teap_pac_pac_attr_info_t; + +/* + * 11.10 and 11.9.10 - PAC Type + */ +typedef struct eap_teap_pac_attr_pac_type_t { + eap_teap_pac_attr_hdr_t hdr; + uint16_t data; /* 1 = Tunnel-PAC */ +} CC_HINT(__packed__) eap_teap_pac_attr_pac_type_t; + +/* RFC 7170, Section 4.2.13 - Crypto-Binding TLV */ +typedef struct eap_tlv_crypto_binding_tlv_t { + uint8_t reserved; + uint8_t version; + uint8_t received_version; + uint8_t subtype; /* Flags[4b] and Sub-Type[4b] */ + uint8_t nonce[32]; + uint8_t emsk_compound_mac[20]; + uint8_t msk_compound_mac[20]; +} CC_HINT(__packed__) eap_tlv_crypto_binding_tlv_t; + +typedef enum eap_teap_tlv_type_t { + EAP_TEAP_TLV_RESERVED_0 = 0, // 0 + EAP_TEAP_TLV_AUTHORITY, // 1 + EAP_TEAP_TLV_IDENTITY, // 2 + EAP_TEAP_TLV_RESULT, // 3 + EAP_TEAP_TLV_NAK, // 4 + EAP_TEAP_TLV_ERROR, // 5 + EAP_TEAP_TLV_CHANNEL_BINDING, // 6 + EAP_TEAP_TLV_VENDOR_SPECIFIC, // 7 + EAP_TEAP_TLV_REQUEST_ACTION, // 8 + EAP_TEAP_TLV_EAP_PAYLOAD, // 9 + EAP_TEAP_TLV_INTERMED_RESULT, // 10 + EAP_TEAP_TLV_PAC, // 11 + EAP_TEAP_TLV_CRYPTO_BINDING, // 12 + EAP_TEAP_TLV_BASIC_PASSWORD_AUTH_REQ, // 13 + EAP_TEAP_TLV_BASIC_PASSWORD_AUTH_RESP, // 14 + EAP_TEAP_TLV_PKCS7, // 15 + EAP_TEAP_TLV_PKCS10, // 16 + EAP_TEAP_TLV_TRUSTED_ROOT, // 17 + EAP_TEAP_TLV_MAX +} eap_teap_tlv_type_t; + +typedef enum eap_teap_tlv_crypto_binding_tlv_flags_t { + EAP_TEAP_TLV_CRYPTO_BINDING_FLAGS_CMAC_EMSK = 1, // 1 + EAP_TEAP_TLV_CRYPTO_BINDING_FLAGS_CMAC_MSK, // 2 + EAP_TEAP_TLV_CRYPTO_BINDING_FLAGS_CMAC_BOTH // 3 +} eap_teap_tlv_crypto_binding_tlv_flags_t; + +typedef enum eap_teap_tlv_crypto_binding_tlv_subtype_t { + EAP_TEAP_TLV_CRYPTO_BINDING_SUBTYPE_REQUEST = 0, // 0 + EAP_TEAP_TLV_CRYPTO_BINDING_SUBTYPE_RESPONSE // 1 +} eap_teap_tlv_crypto_binding_tlv_subtype_t; + +typedef struct teap_imck_t { + uint8_t simck[EAP_TEAP_SIMCK_LEN]; + uint8_t cmk[EAP_TEAP_CMK_LEN]; +} CC_HINT(__packed__) teap_imck_t; +typedef struct teap_tunnel_t { + VALUE_PAIR *username; + VALUE_PAIR *state; + VALUE_PAIR *accept_vps; + bool copy_request_to_tunnel; + bool use_tunneled_reply; + + bool authenticated; + int received_version; + + int mode; + eap_teap_stage_t stage; + + int imckc; + bool imck_emsk_available; + struct teap_imck_t imck_msk; + struct teap_imck_t imck_emsk; + + uint8_t msk[EAP_TEAP_MSK_LEN]; + uint8_t emsk[EAP_TEAP_EMSK_LEN]; + + int default_method; + + uint32_t pac_lifetime; + char const *authority_identity; + uint8_t const *a_id; + uint8_t const *pac_opaque_key; + + struct { + uint8_t *key; + eap_teap_pac_type_t type; + uint32_t expires; + bool expired; + bool send; + } pac; + + bool result_final; + +#ifdef WITH_PROXY + bool proxy_tunneled_request_as_eap; //!< Proxy tunneled session as EAP, or as de-capsulated + //!< protocol. +#endif + char const *virtual_server; +} teap_tunnel_t; + +/* + * Process the TEAP portion of an EAP-TEAP request. + */ +PW_CODE eap_teap_process(eap_handler_t *handler, tls_session_t *tls_session) CC_HINT(nonnull); + +/* + * A bunch of EAP-TEAP helper functions. + */ +VALUE_PAIR *eap_teap_teap2vp(REQUEST *request, UNUSED SSL *ssl, uint8_t const *data, + size_t data_len, DICT_ATTR const *teap_da, vp_cursor_t *out); + +#endif /* _EAP_TEAP_H */ diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap_crypto.c b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap_crypto.c new file mode 100644 index 0000000..17f49f9 --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap_crypto.c @@ -0,0 +1,198 @@ +/* + * teap-crypto.c Cryptographic functions for EAP-TEAP. + * + * Version: $Id$ + * + * Copyright (C) 2022 Network RADIUS SARL <legal@networkradius.com> + * + * This software may not be redistributed in any form without the prior + * written consent of Network RADIUS. + * + * 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. + */ + +RCSID("$Id$") +USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + +#include <stdio.h> +#include <freeradius-devel/libradius.h> + +#include <openssl/evp.h> +#include <openssl/aes.h> +#include <openssl/err.h> + +#include "eap_teap_crypto.h" + +# define DEBUG if (fr_debug_lvl && fr_log_fp) fr_printf_log + +static void debug_errors(void) +{ + unsigned long errCode; + + while((errCode = ERR_get_error())) { + char *err = ERR_error_string(errCode, NULL); + DEBUG("EAP-TEAP error in OpenSSL - %s", err); + } +} + +// https://wiki.openssl.org/index.php/EVP_Authenticated_Encryption_and_Decryption#Authenticated_Encryption_using_GCM_mode +int eap_teap_encrypt(uint8_t const *plaintext, size_t plaintext_len, + uint8_t const *aad, size_t aad_len, + uint8_t const *key, uint8_t *iv, unsigned char *ciphertext, + uint8_t *tag) +{ + EVP_CIPHER_CTX *ctx; + + int len; + + int ciphertext_len; + + + /* Create and initialise the context */ + if (!(ctx = EVP_CIPHER_CTX_new())) { + debug_errors(); + return -1; + }; + + /* Initialise the encryption operation. */ + if (1 != EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) { + debug_errors(); + return -1; + }; + + /* Set IV length if default 12 bytes (96 bits) is not appropriate */ + if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) { + debug_errors(); + return -1; + }; + + /* Initialise key and IV */ + if (1 != EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) { + debug_errors(); + return -1; + }; + + /* Provide any AAD data. This can be called zero or more times as + * required + */ + if (1 != EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) { + debug_errors(); + return -1; + }; + + /* Provide the message to be encrypted, and obtain the encrypted output. + * EVP_EncryptUpdate can be called multiple times if necessary + */ + if (1 != EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) { + debug_errors(); + return -1; + }; + ciphertext_len = len; + + /* Finalise the encryption. Normally ciphertext bytes may be written at + * this stage, but this does not occur in GCM mode + */ + if (1 != EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) { + debug_errors(); + return -1; + }; + ciphertext_len += len; + + /* Get the tag */ + if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, 16, tag)) { + debug_errors(); + return -1; + }; + + /* Clean up */ + EVP_CIPHER_CTX_free(ctx); + + return ciphertext_len; +} + +int eap_teap_decrypt(uint8_t const *ciphertext, size_t ciphertext_len, + uint8_t const *aad, size_t aad_len, + uint8_t const *tag, uint8_t const *key, uint8_t const *iv, uint8_t *plaintext) +{ + EVP_CIPHER_CTX *ctx; + int len; + int plaintext_len; + int ret; + + /* Create and initialise the context */ + if (!(ctx = EVP_CIPHER_CTX_new())) { + debug_errors(); + return -1; + }; + + /* Initialise the decryption operation. */ + if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, NULL, NULL)) { + debug_errors(); + return -1; + }; + + /* Set IV length. Not necessary if this is 12 bytes (96 bits) */ + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, 16, NULL)) { + debug_errors(); + return -1; + }; + + /* Initialise key and IV */ + if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) { + debug_errors(); + return -1; + }; + + /* Provide any AAD data. This can be called zero or more times as + * required + */ + if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len)) { + debug_errors(); + return -1; + }; + + /* Provide the message to be decrypted, and obtain the plaintext output. + * EVP_DecryptUpdate can be called multiple times if necessary + */ + if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) { + debug_errors(); + return -1; + }; + plaintext_len = len; + + { + unsigned char *tmp; + + memcpy(&tmp, &tag, sizeof(tmp)); + + /* Set expected tag value. Works in OpenSSL 1.0.1d and later */ + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, 16, tmp)) { + debug_errors(); + return -1; + }; + } + + /* Finalise the decryption. A positive return value indicates success, + * anything else is a failure - the plaintext is not trustworthy. + */ + ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len); + + /* Clean up */ + EVP_CIPHER_CTX_free(ctx); + + if (ret < 0) return -1; + + /* Success */ + plaintext_len += len; + return plaintext_len; +} diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap_crypto.h b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap_crypto.h new file mode 100644 index 0000000..b02f2b9 --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/eap_teap_crypto.h @@ -0,0 +1,39 @@ +/* + * eap_teap_crypto.h + * + * Version: $Id$ + * + * Copyright (C) 2022 Network RADIUS SARL <legal@networkradius.com> + * + * This software may not be redistributed in any form without the prior + * written consent of Network RADIUS. + * + * 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. + */ + +#ifndef _EAP_TEAP_CRYPTO_H +#define _EAP_TEAP_CRYPTO_H + +RCSIDH(eap_teap_crypto_h, "$Id$") + + +int eap_teap_encrypt(uint8_t const *plaintext, size_t plaintext_len, + uint8_t const *aad, size_t aad_len, + uint8_t const *key, uint8_t *iv, unsigned char *ciphertext, + uint8_t *tag); + +int eap_teap_decrypt(uint8_t const *ciphertext, size_t ciphertext_len, + uint8_t const *aad, size_t aad_len, + uint8_t const *tag, uint8_t const *key, uint8_t const *iv, uint8_t *plaintext); + +#endif /* _EAP_TEAP_CRYPTO_H */ diff --git a/src/modules/rlm_eap/types/rlm_eap_teap/rlm_eap_teap.c b/src/modules/rlm_eap/types/rlm_eap_teap/rlm_eap_teap.c new file mode 100644 index 0000000..5f2a8db --- /dev/null +++ b/src/modules/rlm_eap/types/rlm_eap_teap/rlm_eap_teap.c @@ -0,0 +1,388 @@ +/* + * rlm_eap_teap.c contains the interfaces that are called from eap + * + * Version: $Id$ + * + * Copyright (C) 2022 Network RADIUS SARL <legal@networkradius.com> + * + * This software may not be redistributed in any form without the prior + * written consent of Network RADIUS. + * + * 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. + */ + +RCSID("$Id$") +USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ + +#include "eap_teap.h" + +typedef struct rlm_eap_teap_t { + /* + * TLS configuration + */ + char const *tls_conf_name; + fr_tls_server_conf_t *tls_conf; + + /* + * Default tunneled EAP type + */ + char const *default_method_name; + int default_method; + + /* + * Use the reply attributes from the tunneled session in + * the non-tunneled reply to the client. + */ + bool use_tunneled_reply; + + /* + * Use SOME of the request attributes from outside of the + * tunneled session in the tunneled request + */ + bool copy_request_to_tunnel; + + /* + * Do we do require a client cert? + */ + bool req_client_cert; + + uint32_t pac_lifetime; + char const *authority_identity; + char const *pac_opaque_key; + + /* + * Virtual server for inner tunnel session. + */ + char const *virtual_server; +} rlm_eap_teap_t; + + +static CONF_PARSER module_config[] = { + { "tls", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_teap_t, tls_conf_name), NULL }, + { "default_eap_type", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_teap_t, default_method_name), "md5" }, + { "copy_request_to_tunnel", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_teap_t, copy_request_to_tunnel), "no" }, + { "use_tunneled_reply", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_teap_t, use_tunneled_reply), "no" }, + { "require_client_cert", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_eap_teap_t, req_client_cert), "no" }, + { "pac_lifetime", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_eap_teap_t, pac_lifetime), "604800" }, + { "authority_identity", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_eap_teap_t, authority_identity), NULL }, + { "pac_opaque_key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED, rlm_eap_teap_t, pac_opaque_key), NULL }, + { "virtual_server", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_eap_teap_t, virtual_server), NULL }, + CONF_PARSER_TERMINATOR +}; + + +/* + * Attach the module. + */ +static int mod_instantiate(CONF_SECTION *cs, void **instance) +{ + rlm_eap_teap_t *inst; + + *instance = inst = talloc_zero(cs, rlm_eap_teap_t); + if (!inst) return -1; + + /* + * Parse the configuration attributes. + */ + if (cf_section_parse(cs, inst, module_config) < 0) { + return -1; + } + + if (!inst->virtual_server) { + ERROR("rlm_eap_teap: A 'virtual_server' MUST be defined for security"); + return -1; + } + + /* + * Convert the name to an integer, to make it easier to + * handle. + */ + if (inst->default_method_name && *inst->default_method_name) { + inst->default_method = eap_name2type(inst->default_method_name); + if (inst->default_method < 0) { + ERROR("rlm_eap_teap: Unknown EAP type %s", + inst->default_method_name); + return -1; + } + } + + /* + * Read tls configuration, either from group given by 'tls' + * option, or from the eap-tls configuration. + */ + inst->tls_conf = eaptls_conf_parse(cs, "tls"); + + if (!inst->tls_conf) { + ERROR("rlm_eap_teap: Failed initializing SSL context"); + return -1; + } + + return 0; +} + +/* + * Allocate the TEAP per-session data + */ +static teap_tunnel_t *teap_alloc(TALLOC_CTX *ctx, rlm_eap_teap_t *inst) +{ + teap_tunnel_t *t; + + t = talloc_zero(ctx, teap_tunnel_t); + + t->received_version = -1; + t->default_method = inst->default_method; + t->copy_request_to_tunnel = inst->copy_request_to_tunnel; + t->use_tunneled_reply = inst->use_tunneled_reply; + t->virtual_server = inst->virtual_server; + return t; +} + + +/* + * Send an initial eap-tls request to the peer, using the libeap functions. + */ +static int mod_session_init(void *type_arg, eap_handler_t *handler) +{ + int status; + tls_session_t *ssn; + rlm_eap_teap_t *inst; + VALUE_PAIR *vp; + bool client_cert; + REQUEST *request = handler->request; + + inst = type_arg; + + handler->tls = true; + + /* + * Check if we need a client certificate. + */ + + /* + * EAP-TLS-Require-Client-Cert attribute will override + * the require_client_cert configuration option. + */ + vp = fr_pair_find_by_num(handler->request->config, PW_EAP_TLS_REQUIRE_CLIENT_CERT, 0, TAG_ANY); + if (vp) { + client_cert = vp->vp_integer ? true : false; + } else { + client_cert = inst->req_client_cert; + } + + /* + * Disallow TLS 1.3 for now. + */ + ssn = eaptls_session(handler, inst->tls_conf, client_cert, false); + if (!ssn) { + return 0; + } + + handler->opaque = ((void *)ssn); + + /* + * As TEAP is a unique special snowflake and wants to use its + * own rolling MSK for MPPE we we set the label to NULL so in that + * eaptls_gen_mppe_keys() is NOT called in eaptls_success. + */ + ssn->label = NULL; + + /* + * Really just protocol version. + */ + ssn->peap_flag = EAP_TEAP_VERSION; + + /* + * hostapd's wpa_supplicant gets upset if we include all the + * S+L+O flags but is happy with S+O (TLS payload is zero bytes + * for S anyway) - FIXME not true for early-data TLSv1.3! + */ + ssn->length_flag = false; + + vp = fr_pair_make(ssn, NULL, "FreeRADIUS-EAP-TEAP-Authority-ID", inst->authority_identity, T_OP_EQ); + fr_pair_add(&ssn->outer_tlvs, vp); + + /* + * TLS session initialization is over. Now handle TLS + * related handshaking or application data. + */ + status = eaptls_request(handler->eap_ds, ssn, true); + if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { + REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); + } else { + RDEBUG3("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); + } + if (status == 0) return 0; + + /* + * The next stage to process the packet. + */ + handler->stage = PROCESS; + + return 1; +} + + +/* + * Do authentication, by letting EAP-TLS do most of the work. + */ +static int mod_process(void *arg, eap_handler_t *handler) +{ + int rcode; + int ret = 0; + fr_tls_status_t status; + rlm_eap_teap_t *inst = (rlm_eap_teap_t *) arg; + tls_session_t *tls_session = (tls_session_t *) handler->opaque; + teap_tunnel_t *t = (teap_tunnel_t *) tls_session->opaque; + REQUEST *request = handler->request; + + RDEBUG2("Authenticate"); + + /* + * Process TLS layer until done. + */ + status = eaptls_process(handler); + if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { + REDEBUG("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); + } else { + RDEBUG3("[eaptls process] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); + } + + /* + * Make request available to any SSL callbacks + */ + SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, request); + switch (status) { + /* + * EAP-TLS handshake was successful, tell the + * client to keep talking. + * + * If this was EAP-TLS, we would just return + * an EAP-TLS-Success packet here. + */ + case FR_TLS_SUCCESS: + if (SSL_session_reused(tls_session->ssl)) { + RDEBUG("Skipping Phase2 due to session resumption"); + goto do_keys; + } + + if (t && t->authenticated) { + if (t->accept_vps) { + RDEBUG2("Using saved attributes from the original Access-Accept"); + rdebug_pair_list(L_DBG_LVL_2, request, t->accept_vps, NULL); + fr_pair_list_mcopy_by_num(handler->request->reply, + &handler->request->reply->vps, + &t->accept_vps, 0, 0, TAG_ANY); + } else if (t->use_tunneled_reply) { + RDEBUG2("No saved attributes in the original Access-Accept"); + } + + do_keys: + /* + * Success: Automatically return MPPE keys. + */ + ret = eaptls_success(handler, 0); + goto done; + } + goto phase2; + + /* + * The TLS code is still working on the TLS + * exchange, and it's a valid TLS request. + * do nothing. + */ + case FR_TLS_HANDLED: + ret = 1; + goto done; + + /* + * Handshake is done, proceed with decoding tunneled + * data. + */ + case FR_TLS_OK: + break; + + /* + * Anything else: fail. + */ + default: + ret = 0; + goto done; + } + +phase2: + /* + * Session is established, proceed with decoding + * tunneled data. + */ + RDEBUG2("Session established. Proceeding to decode tunneled attributes"); + + /* + * We may need TEAP data associated with the session, so + * allocate it here, if it wasn't already alloacted. + */ + if (!tls_session->opaque) { + tls_session->opaque = teap_alloc(tls_session, inst); + t = (teap_tunnel_t *) tls_session->opaque; + if (t->received_version < 0) t->received_version = handler->eap_ds->response->type.data[0] & 0x07; + } + + /* + * Process the TEAP portion of the request. + */ + rcode = eap_teap_process(handler, tls_session); + switch (rcode) { + case PW_CODE_ACCESS_REJECT: + eaptls_fail(handler, 0); + ret = 0; + goto done; + + /* + * Access-Challenge, continue tunneled conversation. + */ + case PW_CODE_ACCESS_CHALLENGE: + eaptls_request(handler->eap_ds, tls_session, false); + ret = 1; + goto done; + + /* + * Success: Automatically return MPPE keys. + */ + case PW_CODE_ACCESS_ACCEPT: + goto do_keys; + + default: + break; + } + + /* + * Something we don't understand: Reject it. + */ + eaptls_fail(handler, 0); + +done: + SSL_set_ex_data(tls_session->ssl, FR_TLS_EX_INDEX_REQUEST, NULL); + + return ret; +} + +/* + * The module name should be the only globally exported symbol. + * That is, everything else should be 'static'. + */ +extern rlm_eap_module_t rlm_eap_teap; +rlm_eap_module_t rlm_eap_teap = { + .name = "eap_teap", + .instantiate = mod_instantiate, /* Create new submodule instance */ + .session_init = mod_session_init, /* Initialise a new EAP session */ + .process = mod_process /* Process next round of EAP method */ +}; diff --git a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c index d327c57..482fdca 100644 --- a/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c +++ b/src/modules/rlm_eap/types/rlm_eap_tls/rlm_eap_tls.c @@ -72,19 +72,6 @@ static int mod_instantiate(CONF_SECTION *cs, void **instance) return -1; } -#ifdef TLS1_3_VERSION - if ((inst->tls_conf->max_version == TLS1_3_VERSION) || - (inst->tls_conf->min_version == TLS1_3_VERSION)) { - WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - WARN("!! Most supplicants do not support EAP-TLS with TLS 1.3"); - WARN("!! Please set tls_max_version = \"1.2\""); - WARN("!! FreeRADIUS only supports TLS 1.3 for special builds of wpa_supplicant and Windows"); - WARN("!! This limitation is likely to change in late 2021."); - WARN("!! If you are using this version of FreeRADIUS after 2021, you will probably need to upgrade"); - WARN("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); - } -#endif - return 0; } @@ -135,7 +122,7 @@ static int mod_session_init(void *type_arg, eap_handler_t *handler) * TLS session initialization is over. Now handle TLS * related handshaking or application data. */ - status = eaptls_start(handler->eap_ds, ssn->peap_flag); + status = eaptls_request(handler->eap_ds, ssn, true); if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); } else { diff --git a/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c b/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c index 4e53c92..6fd3abf 100644 --- a/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c +++ b/src/modules/rlm_eap/types/rlm_eap_ttls/rlm_eap_ttls.c @@ -155,12 +155,10 @@ static ttls_tunnel_t *ttls_alloc(TALLOC_CTX *ctx, rlm_eap_ttls_t *inst) */ static int mod_session_init(void *type_arg, eap_handler_t *handler) { - int status; tls_session_t *ssn; rlm_eap_ttls_t *inst; VALUE_PAIR *vp; bool client_cert; - REQUEST *request = handler->request; inst = type_arg; @@ -204,13 +202,7 @@ static int mod_session_init(void *type_arg, eap_handler_t *handler) * TLS session initialization is over. Now handle TLS * related handshaking or application data. */ - status = eaptls_start(handler->eap_ds, ssn->peap_flag); - if ((status == FR_TLS_INVALID) || (status == FR_TLS_FAIL)) { - REDEBUG("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); - } else { - RDEBUG3("[eaptls start] = %s", fr_int2str(fr_tls_status_table, status, "<INVALID>")); - } - if (status == 0) return 0; + eaptls_start(handler->eap_ds, ssn->peap_flag); /* * The next stage to process the packet. @@ -284,7 +276,7 @@ static int mod_process(void *arg, eap_handler_t *handler) ret = eaptls_success(handler, 0); goto done; } else { - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); } ret = 1; goto done; @@ -341,7 +333,7 @@ static int mod_process(void *arg, eap_handler_t *handler) * Access-Challenge, continue tunneled conversation. */ case PW_CODE_ACCESS_CHALLENGE: - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); ret = 1; goto done; diff --git a/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c b/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c index cbe4239..d997e3e 100644 --- a/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c +++ b/src/modules/rlm_eap/types/rlm_eap_ttls/ttls.c @@ -877,6 +877,8 @@ static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data request->proxy_reply = talloc_steal(request, fake->reply); fake->reply = NULL; + request->proxy->dst_port = 0; /* hacks for state.c lookups */ + /* * And we're done with this request. */ @@ -914,7 +916,7 @@ static int CC_HINT(nonnull) eapttls_postproxy(eap_handler_t *handler, void *data case RLM_MODULE_HANDLED: RDEBUG("Reply was handled"); - eaptls_request(handler->eap_ds, tls_session); + eaptls_request(handler->eap_ds, tls_session, false); request->proxy_reply->code = PW_CODE_ACCESS_CHALLENGE; return 1; diff --git a/src/modules/rlm_expr/rlm_expr.c b/src/modules/rlm_expr/rlm_expr.c index 1aad02d..c5ebd34 100644 --- a/src/modules/rlm_expr/rlm_expr.c +++ b/src/modules/rlm_expr/rlm_expr.c @@ -681,7 +681,7 @@ static ssize_t randstr_xlat(UNUSED void *instance, UNUSED REQUEST *request, return -1; } - if (number > 0) { + if (number > 1) { number--; goto redo; } diff --git a/src/modules/rlm_files/rlm_files.c b/src/modules/rlm_files/rlm_files.c index 08679e6..2d4e871 100644 --- a/src/modules/rlm_files/rlm_files.c +++ b/src/modules/rlm_files/rlm_files.c @@ -347,7 +347,6 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const * { char const *name; VALUE_PAIR *check_tmp = NULL; - VALUE_PAIR *reply_tmp = NULL; PAIR_LIST const *user_pl, *default_pl; bool found = false; PAIR_LIST my_pl; @@ -411,6 +410,8 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const * default_pl = default_pl->next; } + RDEBUG3("%s: Checking entry %s at line %d", filename, pl->name, pl->lineno); + if (pl->check) { check_tmp = fr_pair_list_copy(request, pl->check); for (vp = fr_cursor_init(&cursor, &check_tmp); @@ -425,32 +426,35 @@ static rlm_rcode_t file_common(rlm_files_t *inst, REQUEST *request, char const * } if (paircompare(request, request_packet->vps, check_tmp, &reply_packet->vps) == 0) { + VALUE_PAIR *reply_tmp = NULL; + RDEBUG2("%s: Matched entry %s at line %d", filename, pl->name, pl->lineno); found = true; /* ctx may be reply or proxy */ reply_tmp = fr_pair_list_copy(reply_packet, pl->reply); + + if (reply_tmp) fr_pair_delete_by_num(&reply_tmp, PW_FALL_THROUGH, 0, TAG_ANY); if (reply_tmp) radius_pairmove(request, &reply_packet->vps, reply_tmp, true); fr_pair_list_move(request, &request->config, &check_tmp, T_OP_ADD); - fr_pair_list_free(&check_tmp); /* - * Fallthrough? + * Check Fall-Through against the original reply list, which has Fall-Through */ if (!fall_through(pl->reply)) break; } - } - /* - * Remove server internal parameters. - */ - fr_pair_delete_by_num(&reply_packet->vps, PW_FALL_THROUGH, 0, TAG_ANY); + /* + * Always free check_tmp, even if the paircompare() didn't match. + */ + fr_pair_list_free(&check_tmp); + } /* * See if we succeeded. */ - if (!found) return RLM_MODULE_NOOP; /* on to the next module */ + if (!found) return RLM_MODULE_NOOP; /* on to the next module */ return RLM_MODULE_OK; diff --git a/src/modules/rlm_json/rlm_json.c b/src/modules/rlm_json/rlm_json.c index 9cf99d8..a2476e8 100644 --- a/src/modules/rlm_json/rlm_json.c +++ b/src/modules/rlm_json/rlm_json.c @@ -81,7 +81,7 @@ static CONF_PARSER const module_config[] = { * @param outlen space available for the output * @return length of output generated */ -static ssize_t json_encode_xlat(UNUSED void * instance, REQUEST *request, char const *fmt, +static ssize_t json_encode_xlat(void * instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { rlm_json_t const *inst = instance; diff --git a/src/modules/rlm_ldap/ldap.c b/src/modules/rlm_ldap/ldap.c index c356921..d35cb20 100644 --- a/src/modules/rlm_ldap/ldap.c +++ b/src/modules/rlm_ldap/ldap.c @@ -1295,8 +1295,9 @@ void rlm_ldap_check_reply(rlm_ldap_t const *inst, REQUEST *request) !fr_pair_find_by_num(request->config, PW_CRYPT_PASSWORD, 0, TAG_ANY)) { RWDEBUG("No \"known good\" password added. Ensure the admin user has permission to " "read the password attribute"); - RWDEBUG("PAP authentication will *NOT* work with Active Directory (if that is what you " + RWDEBUG("CHAP / MS-CHAP authentication will *NOT* work with Active Directory (if that is what you " "were trying to configure)"); + RWDEBUG("PAP authentication to Active Directory *MUST* set 'Auth-Type := LDAP'"); } } } diff --git a/src/modules/rlm_mschap/rlm_mschap.c b/src/modules/rlm_mschap/rlm_mschap.c index 00ab90d..4742f9f 100644 --- a/src/modules/rlm_mschap/rlm_mschap.c +++ b/src/modules/rlm_mschap/rlm_mschap.c @@ -1013,16 +1013,16 @@ ntlm_auth_err: return -1; } - if (!EVP_CIPHER_CTX_set_key_length(ctx, nt_password->vp_length)) { - REDEBUG("Failed setting key length"); - goto error; - } - if (!EVP_EncryptInit_ex(ctx, EVP_rc4(), NULL, nt_password->vp_octets, NULL)) { REDEBUG("Failed setting key value"); goto error;; } + if (!EVP_CIPHER_CTX_set_key_length(ctx, nt_password->vp_length)) { + REDEBUG("Failed setting key length"); + goto error; + } + if (!EVP_EncryptUpdate(ctx, nt_pass_decrypted, &ntlen, new_nt_password, ntlen)) { REDEBUG("Failed getting output"); goto error; diff --git a/src/modules/rlm_python/rlm_python.c b/src/modules/rlm_python/rlm_python.c index 2adba0e..412859c 100644 --- a/src/modules/rlm_python/rlm_python.c +++ b/src/modules/rlm_python/rlm_python.c @@ -996,6 +996,7 @@ static void *dlopen_libpython(int flags) static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) { int i; + bool locked = false; /* * Explicitly load libpython, so symbols will be available to lib-dynload modules @@ -1023,6 +1024,7 @@ static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) Py_InitializeEx(0); /* Don't override signal handlers - noop on subs calls */ PyEval_InitThreads(); /* This also grabs a lock (which we then need to release) */ main_interpreter = PyThreadState_Get(); /* Store reference to the main interpreter */ + locked = true; } rad_assert(PyEval_ThreadsInitialized()); @@ -1041,6 +1043,7 @@ static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) inst->sub_interpreter = main_interpreter; } + if (!locked) PyEval_AcquireThread(inst->sub_interpreter); PyThreadState_Swap(inst->sub_interpreter); /* diff --git a/src/modules/rlm_python3/rlm_python3.c b/src/modules/rlm_python3/rlm_python3.c index aaa43ab..48deaa3 100644 --- a/src/modules/rlm_python3/rlm_python3.c +++ b/src/modules/rlm_python3/rlm_python3.c @@ -56,11 +56,11 @@ RCSID("$Id$") static uint32_t python_instances = 0; static void *python_dlhandle; -static PyThreadState *main_interpreter; //!< Main interpreter (cext safe) -static PyObject *main_module; //!< Pthon configuration dictionary. +static PyThreadState *main_interpreter = NULL; //!< Main interpreter (cext safe) +static PyObject *main_module = NULL; //!< Pthon configuration dictionary. -static rlm_python_t *current_inst; //!< Needed to pass parameter to PyInit_radiusd -static CONF_SECTION *current_conf; //!< Needed to pass parameter to PyInit_radiusd +static rlm_python_t *current_inst = NULL; //!< Needed to pass parameter to PyInit_radiusd +static CONF_SECTION *current_conf = NULL; //!< Needed to pass parameter to PyInit_radiusd /* * A mapping of configuration file names to internal variables. @@ -387,6 +387,7 @@ static int mod_populate_vptuple(PyObject *pPair, VALUE_PAIR *vp) ERROR("%s:%d, vp->da->name: %s", __func__, __LINE__, vp->da->name); if (PyErr_Occurred()) { python_error_log(); + PyErr_Clear(); } return -1; @@ -567,6 +568,7 @@ static rlm_rcode_t do_python_single(REQUEST *request, PyObject *pFunc, char cons ERROR("%s:%d, %s - pRet is NULL", __func__, __LINE__, funcname); if (PyErr_Occurred()) { python_error_log(); + PyErr_Clear(); } ret = RLM_MODULE_FAIL; goto finish; @@ -674,17 +676,22 @@ finish: if (ret == RLM_MODULE_FAIL) { ERROR("%s:%d, %s - RLM_MODULE_FAIL", __func__, __LINE__, funcname); } + + if (PyErr_Occurred()) { + ERROR("Unhandled Python exception (see below); clearing."); + python_error_log(); + PyErr_Clear(); + } + return ret; } static void python_interpreter_free(PyThreadState *interp) { -DIAG_OFF(deprecated-declarations) - PyEval_AcquireLock(); + PyEval_RestoreThread(interp); PyThreadState_Swap(interp); Py_EndInterpreter(interp); - PyEval_ReleaseLock(); -DIAG_ON(deprecated-declarations) + PyEval_SaveThread(); } /** Destroy a thread state @@ -1094,6 +1101,8 @@ static PyMODINIT_FUNC PyInit_radiusd(void) */ static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) { + bool locked = false; + /* * prepare radiusd module to be loaded */ @@ -1115,7 +1124,33 @@ static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) python_dlhandle = dlopen_libpython(RTLD_NOW | RTLD_GLOBAL); if (!python_dlhandle) WARN("Failed loading libpython symbols into global symbol table"); -#if PY_VERSION_HEX >= 0x03050000 +#if PY_VERSION_HEX > 0x030a0000 + { + PyStatus status; + PyConfig config; + wchar_t *name; + + /* + * Isolated config: Don't override signal handlers - noop on subs calls + */ + PyConfig_InitIsolatedConfig(&config); + + MEM(name = Py_DecodeLocale(main_config.name, NULL)); + status = PyConfig_SetString(&config, &config.program_name, name); + PyMem_RawFree(name); + if (PyStatus_Exception(status)) { + PyConfig_Clear(&config); + return -1; + } + + status = Py_InitializeFromConfig(&config); + if (PyStatus_Exception(status)) { + PyConfig_Clear(&config); + return -1; + } + PyConfig_Clear(&config); + } +#elif PY_VERSION_HEX >= 0x03050000 { wchar_t *name; @@ -1132,9 +1167,12 @@ static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) } #endif +#if PY_VERSION_HEX <= 0x030a0000 Py_InitializeEx(0); /* Don't override signal handlers - noop on subs calls */ PyEval_InitThreads(); /* This also grabs a lock (which we then need to release) */ +#endif main_interpreter = PyThreadState_Get(); /* Store reference to the main interpreter */ + locked = true; } rad_assert(PyEval_ThreadsInitialized()); @@ -1153,6 +1191,7 @@ static int python_interpreter_init(rlm_python_t *inst, CONF_SECTION *conf) inst->sub_interpreter = main_interpreter; } + if (!locked) PyEval_AcquireThread(inst->sub_interpreter); PyThreadState_Swap(inst->sub_interpreter); /* diff --git a/src/modules/rlm_radutmp/rlm_radutmp.c b/src/modules/rlm_radutmp/rlm_radutmp.c index b3d0037..999e6c4 100644 --- a/src/modules/rlm_radutmp/rlm_radutmp.c +++ b/src/modules/rlm_radutmp/rlm_radutmp.c @@ -489,7 +489,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_accounting(void *instance, REQUEST *requ * easier than searching through the entire file. */ if (!cache) { - cache = talloc_zero(NULL, NAS_PORT); + cache = talloc_zero(inst, NAS_PORT); if (cache) { cache->nasaddr = ut.nas_address; cache->port = ut.nas_port; diff --git a/src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c b/src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c index a5c3b93..da7d7fc 100644 --- a/src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c +++ b/src/modules/rlm_sql/drivers/rlm_sql_freetds/rlm_sql_freetds.c @@ -458,7 +458,7 @@ static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t * for (i = 0; i < colcount; i++) { /* Space to hold the result data */ - rowdata[i] = talloc_array(rowdata, char, MAX_DATASTR_LEN + 1); + rowdata[i] = talloc_zero_array(rowdata, char, MAX_DATASTR_LEN + 1); /* Associate the target buffer with the data */ if (ct_bind(conn->command, i + 1, &descriptor, rowdata[i], NULL, NULL) != CS_SUCCEED) { diff --git a/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c b/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c index 78d1b8f..2f51e0a 100644 --- a/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c +++ b/src/modules/rlm_sql/drivers/rlm_sql_mysql/rlm_sql_mysql.c @@ -305,14 +305,11 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c * We need to know about connection errors, and are capable * of reconnecting automatically. */ -#if MYSQL_VERSION_ID >= 50013 { int reconnect = 0; mysql_options(&(conn->db), MYSQL_OPT_RECONNECT, &reconnect); } -#endif -#if (MYSQL_VERSION_ID >= 50000) if (config->query_timeout) { unsigned int connect_timeout = config->query_timeout; unsigned int read_timeout = config->query_timeout; @@ -340,13 +337,8 @@ static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t *c mysql_options(&(conn->db), MYSQL_OPT_READ_TIMEOUT, &read_timeout); mysql_options(&(conn->db), MYSQL_OPT_WRITE_TIMEOUT, &write_timeout); } -#endif -#if (MYSQL_VERSION_ID >= 40100) sql_flags = CLIENT_MULTI_RESULTS | CLIENT_FOUND_ROWS; -#else - sql_flags = CLIENT_FOUND_ROWS; -#endif #ifdef CLIENT_MULTI_STATEMENTS sql_flags |= CLIENT_MULTI_STATEMENTS; @@ -485,14 +477,12 @@ retry_store_result: if (!conn->result) { rcode = sql_check_error(conn->sock, 0); if (rcode != RLM_SQL_OK) return rcode; -#if (MYSQL_VERSION_ID >= 40100) ret = mysql_next_result(conn->sock); if (ret == 0) { /* there are more results */ goto retry_store_result; } else if (ret > 0) return sql_check_error(NULL, ret); /* ret == -1 signals no more results */ -#endif } return RLM_SQL_OK; } @@ -502,17 +492,10 @@ static int sql_num_fields(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *con int num = 0; rlm_sql_mysql_conn_t *conn = handle->conn; -#if MYSQL_VERSION_ID >= 32224 /* * Count takes a connection handle */ if (!(num = mysql_field_count(conn->sock))) { -#else - /* - * Fields takes a result struct - */ - if (!(num = mysql_num_fields(conn->result))) { -#endif return -1; } return num; @@ -576,7 +559,6 @@ retry_fetch_row: rcode = sql_check_error(conn->sock, 0); if (rcode != RLM_SQL_OK) return rcode; -#if (MYSQL_VERSION_ID >= 40100) sql_free_result(handle, config); ret = mysql_next_result(conn->sock); @@ -587,7 +569,7 @@ retry_fetch_row: } } else if (ret > 0) return sql_check_error(NULL, ret); /* If ret is -1 then there are no more rows */ -#endif + return RLM_SQL_NO_MORE_ROWS; } @@ -769,7 +751,6 @@ static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], size_t outlen, */ static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t *config) { -#if (MYSQL_VERSION_ID >= 40100) rlm_sql_mysql_conn_t *conn = handle->conn; int ret; MYSQL_RES *result; @@ -809,7 +790,6 @@ static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, rlm_sql_config_t * mysql_free_result(result); } if (ret > 0) return sql_check_error(NULL, ret); -#endif return RLM_SQL_OK; } diff --git a/src/modules/rlm_sql/drivers/rlm_sql_null/rlm_sql_null.c b/src/modules/rlm_sql/drivers/rlm_sql_null/rlm_sql_null.c index 8f3a8f0..a4c961c 100644 --- a/src/modules/rlm_sql/drivers/rlm_sql_null/rlm_sql_null.c +++ b/src/modules/rlm_sql/drivers/rlm_sql_null/rlm_sql_null.c @@ -53,9 +53,9 @@ static sql_rcode_t sql_select_query(UNUSED rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t *config, UNUSED char const *query) { if (rad_debug_lvl >= L_DBG_LVL_1) { - radlog(L_DBG | L_WARN, "The 'rlm_sql_null' driver CANNOT be used for SELECTS."); - radlog(L_DBG | L_WARN, "Please update the 'sql' module configuration to use a real database."); - radlog(L_DBG | L_WARN, "Set 'driver = ...' to the database you want to use."); + radlog(L_DBG, "The 'rlm_sql_null' driver CANNOT be used for SELECTS."); + radlog(L_DBG, "Please update the 'sql' module configuration to use a real database."); + radlog(L_DBG, "Set 'driver = ...' to the database you want to use."); } return 0; diff --git a/src/modules/rlm_sql/drivers/rlm_sql_sqlite/rlm_sql_sqlite.c b/src/modules/rlm_sql/drivers/rlm_sql_sqlite/rlm_sql_sqlite.c index 65b7d9a..3b2a01b 100644 --- a/src/modules/rlm_sql/drivers/rlm_sql_sqlite/rlm_sql_sqlite.c +++ b/src/modules/rlm_sql/drivers/rlm_sql_sqlite/rlm_sql_sqlite.c @@ -232,9 +232,9 @@ static void sql_print_error(sqlite3 *db, int status, char const *fmt, ...) static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename) { ssize_t len; - int statement_cnt = 0; + int statement_len, statement_cnt = 0; char *buffer; - char *p, *q; + char const *p; int cl; FILE *f; struct stat finfo; @@ -308,7 +308,7 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename) if ((*p != 0x0a) && (*p != 0x0d) && (*p != '\t')) break; cl = 1; } else { - cl = fr_utf8_char((uint8_t *) p, -1); + cl = fr_utf8_char((uint8_t const *) p, -1); if (!cl) break; } } @@ -319,21 +319,13 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename) return -1; } - /* - * Statement delimiter is ;\n - */ p = buffer; - while ((q = strchr(p, ';'))) { - if ((q[1] != '\n') && (q[1] != '\0')) { - p = q + 1; - statement_cnt++; - continue; - } - + while (*p) { + statement_len = len - (p - buffer); #ifdef HAVE_SQLITE3_PREPARE_V2 - status = sqlite3_prepare_v2(db, p, q - p, &statement, &z_tail); + status = sqlite3_prepare_v2(db, p, statement_len, &statement, &z_tail); #else - status = sqlite3_prepare(db, p, q - p, &statement, &z_tail); + status = sqlite3_prepare(db, p, statement_len, &statement, &z_tail); #endif if (sql_check_error(db, status) != RLM_SQL_OK) { @@ -342,6 +334,11 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename) return -1; } + /* + * No SQL statement was found + */ + if (!statement) break; + status = sqlite3_step(statement); if (sql_check_error(db, status) != RLM_SQL_OK) { sql_print_error(db, status, "Failed executing statement %i", statement_cnt); @@ -358,7 +355,7 @@ static int sql_loadfile(TALLOC_CTX *ctx, sqlite3 *db, char const *filename) } statement_cnt++; - p = q + 1; + p = z_tail; } talloc_free(buffer); diff --git a/src/modules/rlm_sql_map/rlm_sql_map.c b/src/modules/rlm_sql_map/rlm_sql_map.c index b6a27e5..28e2634 100644 --- a/src/modules/rlm_sql_map/rlm_sql_map.c +++ b/src/modules/rlm_sql_map/rlm_sql_map.c @@ -32,8 +32,6 @@ RCSID("$Id$") #include <rlm_sql.h> -#define MAX_QUERY_LEN 2048 - typedef struct rlm_sql_map_t { char const *sql_instance_name; //!< Instance of SQL module to use, //!< usually just 'sql'. diff --git a/src/modules/rlm_sqlcounter/rlm_sqlcounter.c b/src/modules/rlm_sqlcounter/rlm_sqlcounter.c index 5987ae6..cd41ac4 100644 --- a/src/modules/rlm_sqlcounter/rlm_sqlcounter.c +++ b/src/modules/rlm_sqlcounter/rlm_sqlcounter.c @@ -134,7 +134,7 @@ static int find_next_reset(rlm_sqlcounter_t *inst, REQUEST *request, time_t time } num = atoi(inst->reset); - DEBUG("rlm_sqlcounter: num=%d, last=%c",num,last); + DEBUG3("rlm_sqlcounter: num=%d, last=%c",num,last); } if (strcmp(inst->reset, "hourly") == 0 || last == 'h') { @@ -181,10 +181,10 @@ static int find_next_reset(rlm_sqlcounter_t *inst, REQUEST *request, time_t time if (len == 0) *sNextTime = '\0'; if (is_monthly) { - DEBUG("rlm_sqlcounter: Current Time: %" PRId64 " [%s], Next reset %" PRId64 " [%s], Reset day [%d]", + DEBUG2("rlm_sqlcounter: Current Time: %" PRId64 " [%s], Next reset %" PRId64 " [%s], Reset day [%d]", (int64_t) timeval, sCurrentTime, (int64_t) inst->reset_time, sNextTime, inst->reset_day); } else { - DEBUG("rlm_sqlcounter: Current Time: %" PRId64 " [%s], Next reset %" PRId64 " [%s]", + DEBUG2("rlm_sqlcounter: Current Time: %" PRId64 " [%s], Next reset %" PRId64 " [%s]", (int64_t) timeval, sCurrentTime, (int64_t) inst->reset_time, sNextTime); } return ret; @@ -220,7 +220,7 @@ static int find_prev_reset(rlm_sqlcounter_t *inst, time_t timeval) if (!isalpha((uint8_t) last)) last = 'd'; num = atoi(inst->reset); - DEBUG("rlm_sqlcounter: num=%d, last=%c",num,last); + DEBUG3("rlm_sqlcounter: num=%d, last=%c",num,last); } if (strcmp(inst->reset, "hourly") == 0 || last == 'h') { diff --git a/src/modules/rlm_sqlippool/rlm_sqlippool.c b/src/modules/rlm_sqlippool/rlm_sqlippool.c index 22e9381..cf8d9d0 100644 --- a/src/modules/rlm_sqlippool/rlm_sqlippool.c +++ b/src/modules/rlm_sqlippool/rlm_sqlippool.c @@ -66,30 +66,15 @@ typedef struct rlm_sqlippool_t { char const *pool_check; //!< Query to check for the existence of the pool. - /* Start sequence */ - char const *start_begin; //!< SQL query to begin. - char const *start_update; //!< SQL query to update an IP entry. - char const *start_commit; //!< SQL query to commit. - - /* Alive sequence */ - char const *alive_begin; //!< SQL query to begin. - char const *alive_update; //!< SQL query to update an IP entry. - char const *alive_commit; //!< SQL query to commit. - - /* Stop sequence */ - char const *stop_begin; //!< SQL query to begin. - char const *stop_clear; //!< SQL query to clear an IP. - char const *stop_commit; //!< SQL query to commit. - - /* On sequence */ - char const *on_begin; //!< SQL query to begin. - char const *on_clear; //!< SQL query to clear an entire NAS. - char const *on_commit; //!< SQL query to commit. - - /* Off sequence */ - char const *off_begin; //!< SQL query to begin. - char const *off_clear; //!< SQL query to clear an entire NAS. - char const *off_commit; //!< SQL query to commit. + char const *start_update; //!< SQL query run on Accounting Start + + char const *alive_update; //!< SQL query run on Accounting Interim-Update + + char const *stop_clear; //!< SQL query run on Accounting Stop + + char const *on_clear; //!< SQL query run on Accounting On + + char const *off_clear; //!< SQL query run on Accounting Off /* Logging Section */ char const *log_exists; //!< There was an ip address already assigned. @@ -97,10 +82,6 @@ typedef struct rlm_sqlippool_t { char const *log_clear; //!< We successfully deallocated ip address from pool. char const *log_failed; //!< Failed to allocate ip from the pool. char const *log_nopool; //!< There was no Framed-IP-Address but also no Pool-Name. - - /* Reserved to handle 255.255.255.254 Requests */ - char const *defaultpool; //!< Default Pool-Name if there is none in the check items. - } rlm_sqlippool_t; static CONF_PARSER message_config[] = { @@ -131,10 +112,6 @@ static CONF_PARSER module_config[] = { { "pool-name", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, pool_name), NULL }, { "pool_name", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, pool_name), "Pool-Name" }, - { "default-pool", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_DEPRECATED, rlm_sqlippool_t, defaultpool), NULL }, - { "default_pool", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, defaultpool), "main_pool" }, - - { "ipv6", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sqlippool_t, ipv6), NULL}, { "allow_duplicates", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_sqlippool_t, allow_duplicates), NULL}, { "attribute_name", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_sqlippool_t, attribute_name), NULL}, @@ -168,55 +145,21 @@ static CONF_PARSER module_config[] = { { "pool_check", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, pool_check), "" }, - { "start-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_begin), NULL }, - { "start_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, start_begin), "" }, - { "start-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_update), NULL }, { "start_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, start_update), "" }, - { "start-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, start_commit), NULL }, - { "start_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, start_commit), "" }, - - - { "alive-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_begin), NULL }, - { "alive_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, alive_begin), "" }, - { "alive-update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_update), NULL }, { "alive_update", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, alive_update), "" }, - { "alive-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, alive_commit), NULL }, - { "alive_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, alive_commit), "" }, - - - { "stop-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_begin), NULL }, - { "stop_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, stop_begin), "" }, - { "stop-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_clear), NULL }, { "stop_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, stop_clear), "" }, - { "stop-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, stop_commit), NULL }, - { "stop_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, stop_commit), "" }, - - - { "on-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_begin), NULL }, - { "on_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, on_begin), "" }, - { "on-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_clear), NULL }, { "on_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, on_clear), "" }, - { "on-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, on_commit), NULL }, - { "on_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, on_commit), "" }, - - - { "off-begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_begin), NULL }, - { "off_begin", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, off_begin), "" }, - { "off-clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_clear), NULL }, { "off_clear", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT , rlm_sqlippool_t, off_clear), "" }, - { "off-commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT | PW_TYPE_DEPRECATED, rlm_sqlippool_t, off_commit), NULL }, - { "off_commit", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_XLAT, rlm_sqlippool_t, off_commit), "" }, - { "messages", FR_CONF_POINTER(PW_TYPE_SUBSECTION, NULL), (void const *) message_config }, CONF_PARSER_TERMINATOR }; @@ -405,7 +348,7 @@ static int CC_HINT(nonnull (1, 3, 4, 5)) sqlippool_query1(char *out, int outlen, } if (!(*handle)->row) { - REDEBUG("SQL query did not return any results"); + RDEBUG2("SQL query did not return any results"); goto finish; } @@ -416,7 +359,7 @@ static int CC_HINT(nonnull (1, 3, 4, 5)) sqlippool_query1(char *out, int outlen, rlen = strlen((*handle)->row[0]); if (rlen >= outlen) { - RDEBUG("insufficient string space"); + REDEBUG("The first column of the result was too long (%d)", rlen); goto finish; } @@ -757,9 +700,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_post_auth(void *instance, REQUEST *reque static int mod_accounting_start(rlm_sql_handle_t **handle, rlm_sqlippool_t *inst, REQUEST *request) { - DO(start_begin); DO(start_update); - DO(start_commit); return RLM_MODULE_OK; } @@ -769,9 +710,7 @@ static int mod_accounting_alive(rlm_sql_handle_t **handle, { int affected; - DO(alive_begin); DO_AFFECTED(alive_update, affected); - DO(alive_commit); return (affected == 0 ? RLM_MODULE_NOTFOUND : RLM_MODULE_OK); } @@ -779,9 +718,7 @@ static int mod_accounting_alive(rlm_sql_handle_t **handle, static int mod_accounting_stop(rlm_sql_handle_t **handle, rlm_sqlippool_t *inst, REQUEST *request) { - DO(stop_begin); DO(stop_clear); - DO(stop_commit); return do_logging(request, inst->log_clear, RLM_MODULE_OK); } @@ -789,9 +726,7 @@ static int mod_accounting_stop(rlm_sql_handle_t **handle, static int mod_accounting_on(rlm_sql_handle_t **handle, rlm_sqlippool_t *inst, REQUEST *request) { - DO(on_begin); DO(on_clear); - DO(on_commit); return RLM_MODULE_OK; } @@ -799,9 +734,7 @@ static int mod_accounting_on(rlm_sql_handle_t **handle, static int mod_accounting_off(rlm_sql_handle_t **handle, rlm_sqlippool_t *inst, REQUEST *request) { - DO(off_begin); DO(off_clear); - DO(off_commit); return RLM_MODULE_OK; } diff --git a/src/modules/rlm_totp/Makefile b/src/modules/rlm_totp/Makefile index 5ad2ae9..595cea6 100644 --- a/src/modules/rlm_totp/Makefile +++ b/src/modules/rlm_totp/Makefile @@ -22,7 +22,7 @@ freeradius-devel: # ./totp totp <time> <sha1key> <8-character-challenge> # totp: rlm_totp.c | src freeradius-devel - @$(CC) -DTESTING $(CFLAGS) $(OPENSSL_CPPFLAGS) -o $@ $(LDFLAGS) $(LIBS) ../../../build/lib/.libs/libfreeradius-radius.a rlm_totp.c + @$(CC) -DTESTING $(CFLAGS) $(CPPFLAGS) $(OPENSSL_CPPFLAGS) -o $@ $(LDFLAGS) $(LIBS) ../../../build/lib/.libs/libfreeradius-radius.a rlm_totp.c # # Test vectors from RFC 6238, Appendix B diff --git a/src/modules/rlm_totp/rlm_totp.c b/src/modules/rlm_totp/rlm_totp.c index 1459716..d58e1ee 100644 --- a/src/modules/rlm_totp/rlm_totp.c +++ b/src/modules/rlm_totp/rlm_totp.c @@ -26,9 +26,68 @@ RCSID("$Id$") #include <freeradius-devel/radiusd.h> #include <freeradius-devel/modules.h> +#include <freeradius-devel/dlist.h> #include <freeradius-devel/rad_assert.h> -#define TIME_STEP (30) +#include <ctype.h> + +typedef struct { + uint8_t const *key; + size_t keylen; + char const *passwd; + time_t when; + bool unlisted; + void *instance; + fr_dlist_t dlist; +} totp_dedup_t; + +#ifdef HAVE_PTHREAD_H +#include <pthread.h> + +#define PTHREAD_MUTEX_LOCK(_x) pthread_mutex_lock(&((_x)->mutex)) +#define PTHREAD_MUTEX_UNLOCK(_x) pthread_mutex_unlock(&((_x)->mutex)) +#else +#define PTHREAD_MUTEX_LOCK(_x) +#define PTHREAD_MUTEX_UNLOCK(_x) +#endif + + +/* Define a structure for the configuration variables */ +typedef struct rlm_totp_t { + char const *name; //!< name of this instance */ + uint32_t time_step; //!< seconds + uint32_t otp_length; //!< forced to 6 or 8 + uint32_t lookback_steps; //!< number of steps to look back + uint32_t lookback_interval; //!< interval in seconds between steps + uint32_t lookforward_steps; //!< number of steps to look forwards + rbtree_t *dedup_tree; + fr_dlist_t dedup_list; +#ifdef HAVE_PTHREAD_H + pthread_mutex_t mutex; +#endif +} rlm_totp_t; + +#ifndef TESTING +/* Map configuration file names to internal variables */ +static const CONF_PARSER module_config[] = { + { "time_step", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, time_step), "30" }, + { "otp_length", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, otp_length), "6" }, + { "lookback_steps", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, lookback_steps), "1" }, + { "lookback_interval", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, lookback_interval), "30" }, + { "lookforward_steps", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_totp_t, lookforward_steps), "0" }, + CONF_PARSER_TERMINATOR +}; + +#define TIME_STEP (inst->time_step) +#define OTP_LEN (inst->otp_length) +#define BACK_STEPS (steps) +#define BACK_STEP_SECS (inst->lookback_interval) +#else +#define TIME_STEP (30) +#define OTP_LEN (8) +#define BACK_STEPS (1) +#define BACK_STEP_SECS (30) +#endif /* * RFC 4648 base32 decoding. @@ -110,7 +169,7 @@ static ssize_t base32_decode(uint8_t *out, size_t outlen, char const *in) * Will get converted to * * 11111222 22333334 44445555 56666677 77788888 - */ + */ for (p = b = out; p < end; p += 8) { b[0] = p[0] << 3; b[0] |= p[1] >> 2; @@ -142,14 +201,112 @@ static ssize_t base32_decode(uint8_t *out, size_t outlen, char const *in) return b - out; } + #ifndef TESTING -#define LEN 6 -#define PRINT "%06u" -#define DIV 1000000 -#else -#define LEN 8 -#define PRINT "%08u" -#define DIV 100000000 +#define TESTING_UNUSED + +#else /* TESTING */ +#undef RDEBUG3 +#define RDEBUG3(fmt, ...) printf(fmt "\n", ## __VA_ARGS__) +#define TESTING_UNUSED UNUSED +#endif + +#ifndef TESTING +static int mod_bootstrap(CONF_SECTION *conf, void *instance) +{ + rlm_totp_t *inst = instance; + + inst->name = cf_section_name2(conf); + if (!inst->name) { + inst->name = cf_section_name1(conf); + } + + return 0; +} + +static int dedup_cmp(void const *one, void const *two) +{ + int rcode; + totp_dedup_t const *a = one; + totp_dedup_t const *b = two; + + if (a->keylen < b->keylen) return -1; + if (a->keylen > b->keylen) return +1; + + rcode = memcmp(a->key , b->key, a->keylen); + if (rcode != 0) return rcode; + + /* + * The user can enter multiple keys + */ + return strcmp(a->passwd, b->passwd); +} + +static void dedup_free(void *data) +{ + totp_dedup_t *dedup = data; +#ifdef HAVE_PTHREAD_H + rlm_totp_t *inst = dedup->instance; +#endif + + if (!dedup->unlisted) { + PTHREAD_MUTEX_LOCK(inst); + fr_dlist_entry_unlink(&dedup->dlist); + PTHREAD_MUTEX_UNLOCK(inst); + } + + free(dedup); +} + +/* + * Do any per-module initialization that is separate to each + * configured instance of the module. e.g. set up connections + * to external databases, read configuration files, set up + * dictionary entries, etc. + * + * If configuration information is given in the config section + * that must be referenced in later calls, store a handle to it + * in *instance otherwise put a null pointer there. + */ +static int mod_instantiate(UNUSED CONF_SECTION *conf, void *instance) +{ + rlm_totp_t *inst = instance; + + FR_INTEGER_BOUND_CHECK("time_step", inst->time_step, >=, 5); + FR_INTEGER_BOUND_CHECK("time_step", inst->time_step, <=, 120); + + FR_INTEGER_BOUND_CHECK("lookback_steps", inst->lookback_steps, >=, 1); + FR_INTEGER_BOUND_CHECK("lookback_steps", inst->lookback_steps, <=, 10); + + FR_INTEGER_BOUND_CHECK("lookforward_steps", inst->lookforward_steps, <=, 10); + + FR_INTEGER_BOUND_CHECK("lookback_interval", inst->lookback_interval, <=, inst->time_step); + + FR_INTEGER_BOUND_CHECK("otp_length", inst->otp_length, >=, 6); + FR_INTEGER_BOUND_CHECK("otp_length", inst->otp_length, <=, 8); + + if (inst->otp_length == 7) inst->otp_length = 8; + + inst->dedup_tree = rbtree_create(instance, dedup_cmp, dedup_free, 0); + if (!inst->dedup_tree) return -1; + + fr_dlist_entry_init(&inst->dedup_list); +#ifdef HAVE_PTHREAD_H + (void) pthread_mutex_init(&inst->mutex, NULL); +#endif + + return 0; +} + +#ifdef HAVE_PTHREAD_H +static int mod_detach(void *instance) +{ + rlm_totp_t *inst = instance; + + pthread_mutex_destroy(&inst->mutex); + return 0; +} +#endif #endif /* @@ -159,8 +316,14 @@ static ssize_t base32_decode(uint8_t *out, size_t outlen, char const *in) * for 8-character challenges, and not for 6 character * challenges! */ -static int totp_cmp(time_t now, uint8_t const *key, size_t keylen, char const *totp) +static int totp_cmp(TESTING_UNUSED REQUEST *request, time_t now, uint8_t const *key, size_t keylen, char const *totp, TESTING_UNUSED void *instance) { +#ifndef TESTING + rlm_totp_t *inst = instance; + uint32_t steps = inst->lookback_steps > inst->lookforward_steps ? inst->lookback_steps : inst->lookforward_steps; +#endif + time_t diff, then; + unsigned int i; uint8_t offset; uint32_t challenge; uint64_t padded; @@ -168,59 +331,153 @@ static int totp_cmp(time_t now, uint8_t const *key, size_t keylen, char const *t uint8_t data[8]; uint8_t digest[SHA1_DIGEST_LENGTH]; - padded = ((uint64_t) now) / TIME_STEP; - data[0] = padded >> 56; - data[1] = padded >> 48; - data[2] = padded >> 40; - data[3] = padded >> 32; - data[4] = padded >> 24; - data[5] = padded >> 16; - data[6] = padded >> 8; - data[7] = padded & 0xff; - /* - * Encrypt the network order time with the key. + * First try to authenticate against the current OTP, then step + * back in increments of BACK_STEP_SECS, up to BACK_STEPS times, + * to authenticate properly in cases of long transit delay, as + * described in RFC 6238, secion 5.2. */ - fr_hmac_sha1(digest, data, 8, key, keylen); - /* - * Take the least significant 4 bits. - */ - offset = digest[SHA1_DIGEST_LENGTH - 1] & 0x0f; + for (i = 0, diff = 0; i <= BACK_STEPS; i++, diff += BACK_STEP_SECS) { +#ifndef TESTING + if (i > inst->lookback_steps) goto forwards; +#endif + then = now - diff; +#ifndef TESTING + repeat: +#endif + padded = (uint64_t) then / TIME_STEP; + data[0] = padded >> 56; + data[1] = padded >> 48; + data[2] = padded >> 40; + data[3] = padded >> 32; + data[4] = padded >> 24; + data[5] = padded >> 16; + data[6] = padded >> 8; + data[7] = padded & 0xff; + + /* + * Encrypt the network order time with the key. + */ + fr_hmac_sha1(digest, data, 8, key, keylen); + + /* + * Take the least significant 4 bits. + */ + offset = digest[SHA1_DIGEST_LENGTH - 1] & 0x0f; + + /* + * Grab the 32bits at "offset", and drop the high bit. + */ + challenge = (digest[offset] & 0x7f) << 24; + challenge |= digest[offset + 1] << 16; + challenge |= digest[offset + 2] << 8; + challenge |= digest[offset + 3]; + + /* + * The token is the last 6 digits in the number (or 8 for testing).. + */ + snprintf(buffer, sizeof(buffer), ((OTP_LEN == 6) ? "%06u" : "%08u"), + challenge % ((OTP_LEN == 6) ? 1000000 : 100000000)); + + RDEBUG3("Now: %zu, Then: %zu", (size_t) now, (size_t) then); + RDEBUG3("Expected %s", buffer); + RDEBUG3("Received %s", totp); + + if (rad_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, OTP_LEN) == 0) return 0; + +#ifndef TESTING + /* + * We've tested backwards, now do the equivalent time slot forwards + */ + if ((then < now) && (i <= inst->lookforward_steps)) { + forwards: + then = now + diff; + goto repeat; + } +#endif + } + return 1; +} + +#ifndef TESTING + +static inline CC_HINT(nonnull) totp_dedup_t *fr_dlist_head(fr_dlist_t const *head) +{ + if (head->prev == head) return NULL; + + return (totp_dedup_t *) (((uintptr_t) head->next) - offsetof(totp_dedup_t, dlist)); +} + + +static bool totp_reused(void *instance, time_t now, uint8_t const *key, size_t keylen, char const *passwd) +{ + rlm_totp_t *inst = instance; + totp_dedup_t *dedup, my_dedup; + + my_dedup.key = key; + my_dedup.keylen = keylen; + my_dedup.passwd = passwd; + + PTHREAD_MUTEX_LOCK(inst); /* - * Grab the 32bits at "offset", and drop the high bit. + * Expire the oldest entries before searching for an entry in the tree. */ - challenge = (digest[offset] & 0x7f) << 24; - challenge |= digest[offset + 1] << 16; - challenge |= digest[offset + 2] << 8; - challenge |= digest[offset + 3]; + while (true) { + dedup = fr_dlist_head(&inst->dedup_list); + if (!dedup) break; + + if ((now - dedup->when) < (inst->lookback_steps * inst->lookback_interval)) break; + + dedup->unlisted = true; + fr_dlist_entry_unlink(&dedup->dlist); + (void) rbtree_deletebydata(inst->dedup_tree, dedup); + } /* - * The token is the last 6 digits in the number. + * Was this key and TOTP reused? */ - snprintf(buffer, sizeof(buffer), PRINT, challenge % DIV); + dedup = rbtree_finddata(inst->dedup_tree, &my_dedup); + if (dedup) { + PTHREAD_MUTEX_UNLOCK(inst); + return true; + } - return rad_digest_cmp((uint8_t const *) buffer, (uint8_t const *) totp, LEN); -} + dedup = calloc(sizeof(*dedup), 1); + if (!dedup) { + PTHREAD_MUTEX_UNLOCK(inst); + return false; + } -#ifndef TESTING + dedup->key = key; + dedup->keylen = keylen; + dedup->passwd = passwd; + dedup->when = now; + dedup->instance = inst; + + fr_dlist_insert_tail(&inst->dedup_list, &dedup->dlist); + (void) rbtree_insert(inst->dedup_tree, dedup); + PTHREAD_MUTEX_UNLOCK(inst); + + return false; +} /* * Do the authentication */ -static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQUEST *request) +static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(void *instance, REQUEST *request) { VALUE_PAIR *vp, *password; uint8_t const *key; size_t keylen; uint8_t buffer[80]; /* multiple of 5*8 characters */ - + uint64_t now = time(NULL); password = fr_pair_find_by_num(request->packet->vps, PW_TOTP_PASSWORD, 0, TAG_ANY); if (!password) return RLM_MODULE_NOOP; - if (password->vp_length != 6) { + if ((password->vp_length != 6) && (password->vp_length != 8)) { RDEBUG("TOTP-Password has incorrect length %d", (int) password->vp_length); return RLM_MODULE_FAIL; } @@ -237,11 +494,13 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU ssize_t len; vp = fr_pair_find_by_num(request->config, PW_TOTP_SECRET, 0, TAG_ANY); - if (!vp) return RLM_MODULE_NOOP; - + if (!vp) { + RDEBUG("TOTP mod_authenticate() did not receive a TOTP-Secret"); + return RLM_MODULE_NOOP; + } len = base32_decode(buffer, sizeof(buffer), vp->vp_strvalue); if (len < 0) { - RDEBUG("TOTP-Secret cannot be decoded"); + REDEBUG("TOTP-Secret cannot be decoded"); return RLM_MODULE_FAIL; } @@ -249,9 +508,25 @@ static rlm_rcode_t CC_HINT(nonnull) mod_authenticate(UNUSED void *instance, REQU keylen = len; } - if (totp_cmp(time(NULL), key, keylen, password->vp_strvalue) != 0) return RLM_MODULE_FAIL; + vp = fr_pair_find_by_num(request->config, PW_TOTP_TIME_OFFSET, 0, TAG_ANY); + if (vp && (vp->vp_signed > -600) && (vp->vp_signed < 600)) { + RDEBUG("Using TOTP-Time-Offset = %d", vp->vp_signed); + now += vp->vp_signed; + } + + if (totp_cmp(request, now, key, keylen, password->vp_strvalue, instance) == 0) { + /* + * Forbid using a key more than once. + */ + if (totp_reused(instance, now, key, keylen, password->vp_strvalue)) return RLM_MODULE_REJECT; + + return RLM_MODULE_OK; + } - return RLM_MODULE_OK; + /* + * Bad keys don't affect the cache. + */ + return RLM_MODULE_REJECT; } @@ -269,12 +544,24 @@ module_t rlm_totp = { .magic = RLM_MODULE_INIT, .name = "totp", .type = RLM_TYPE_THREAD_SAFE, + .inst_size = sizeof(rlm_totp_t), + .config = module_config, + .bootstrap = mod_bootstrap, + .instantiate = mod_instantiate, +#ifdef HAVE_PTHREAD_H + .detach = mod_detach, +#endif .methods = { [MOD_AUTHENTICATE] = mod_authenticate, }, }; #else /* TESTING */ +/* + * ./totp decode KEY_BASE32 + * + * ./totp totp now KEY TOTP + */ int main(int argc, char **argv) { size_t len; @@ -298,23 +585,31 @@ int main(int argc, char **argv) } /* - * TOTP <time> <key> <8-character-expected-token> + * TOTP <time> <key> <expected-token> */ if (strcmp(argv[1], "totp") == 0) { uint64_t now; if (argc < 5) return 0; - (void) sscanf(argv[2], "%llu", &now); + if (strcmp(argv[2], "now") == 0) { + now = time(NULL); + } else { + (void) sscanf(argv[2], "%llu", &now); + } + + printf ("=== Time = %llu, TIME_STEP = %d, BACK_STEPS = %d, BACK_STEP_SECS = %d ===\n", + now, TIME_STEP, BACK_STEPS, BACK_STEP_SECS); - if (totp_cmp((time_t) now, (uint8_t const *) argv[3], strlen(argv[3]), argv[4]) == 0) { - return 0; + if (totp_cmp(NULL, (time_t) now, (uint8_t const *) argv[3], + strlen(argv[3]), argv[4], NULL) == 0) { + return 0; } printf("Fail\n"); return 1; } - fprintf(stderr, "Unknown command argv[1]\n", argv[1]); + fprintf(stderr, "Unknown command %s\n", argv[1]); return 1; } #endif diff --git a/src/modules/rlm_unbound/rlm_unbound.c b/src/modules/rlm_unbound/rlm_unbound.c index dddc3bf..c5e837a 100644 --- a/src/modules/rlm_unbound/rlm_unbound.c +++ b/src/modules/rlm_unbound/rlm_unbound.c @@ -709,7 +709,7 @@ static int mod_instantiate(CONF_SECTION *conf, void *instance) return -1; } -static int mod_detach(UNUSED void *instance) +static int mod_detach(void *instance) { rlm_unbound_t *inst = instance; diff --git a/src/modules/rlm_unpack/rlm_unpack.c b/src/modules/rlm_unpack/rlm_unpack.c index dfdc81a..03cdb92 100644 --- a/src/modules/rlm_unpack/rlm_unpack.c +++ b/src/modules/rlm_unpack/rlm_unpack.c @@ -287,7 +287,7 @@ static ssize_t substring_xlat(UNUSED void *instance, REQUEST *request, /* * Trim whitespace */ - while (isspace((uint8_t) *p) && p++); + while (isspace((uint8_t) *p)) p++; /* * Find numeric parameters at the end. diff --git a/src/modules/rlm_wimax/rlm_wimax.c b/src/modules/rlm_wimax/rlm_wimax.c index d2125eb..eb83e25 100644 --- a/src/modules/rlm_wimax/rlm_wimax.c +++ b/src/modules/rlm_wimax/rlm_wimax.c @@ -35,20 +35,13 @@ USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */ #include <freeradius-devel/openssl3.h> #define WIMAX_EPSAKA_RAND_SIZE 16 -#define WIMAX_EPSAKA_KI_SIZE 16 -#define WIMAX_EPSAKA_OPC_SIZE 16 -#define WIMAX_EPSAKA_AMF_SIZE 2 #define WIMAX_EPSAKA_SQN_SIZE 6 -#define WIMAX_EPSAKA_MAC_A_SIZE 8 -#define WIMAX_EPSAKA_MAC_S_SIZE 8 #define WIMAX_EPSAKA_XRES_SIZE 8 #define WIMAX_EPSAKA_CK_SIZE 16 #define WIMAX_EPSAKA_IK_SIZE 16 #define WIMAX_EPSAKA_AK_SIZE 6 -#define WIMAX_EPSAKA_AK_RESYNC_SIZE 6 #define WIMAX_EPSAKA_KK_SIZE 32 #define WIMAX_EPSAKA_KS_SIZE 14 -#define WIMAX_EPSAKA_PLMN_SIZE 3 #define WIMAX_EPSAKA_KASME_SIZE 32 #define WIMAX_EPSAKA_AUTN_SIZE 16 #define WIMAX_EPSAKA_AUTS_SIZE 14 diff --git a/src/modules/rlm_yubikey/decrypt.c b/src/modules/rlm_yubikey/decrypt.c index 20b6df8..2bf4543 100644 --- a/src/modules/rlm_yubikey/decrypt.c +++ b/src/modules/rlm_yubikey/decrypt.c @@ -106,7 +106,7 @@ rlm_rcode_t rlm_yubikey_decrypt(rlm_yubikey_t *inst, REQUEST *request, char cons * Combine the two counter fields together so we can do * replay attack checks. */ - counter = (yubikey_counter(token.ctr) << 16) | token.use; + counter = (yubikey_counter(token.ctr) << 8) | token.use; vp = fr_pair_make(request->packet, &request->packet->vps, "Yubikey-Counter", NULL, T_OP_SET); if (!vp) { diff --git a/src/modules/rlm_yubikey/rlm_yubikey.c b/src/modules/rlm_yubikey/rlm_yubikey.c index 83b7655..5bbed11 100644 --- a/src/modules/rlm_yubikey/rlm_yubikey.c +++ b/src/modules/rlm_yubikey/rlm_yubikey.c @@ -77,19 +77,16 @@ static ssize_t modhex2hex(char const *modhex, uint8_t *hex, size_t len) size_t i; char *c1, *c2; - for (i = 0; i < len; i++) { - if (modhex[i << 1] == '\0') { - break; - } + for (i = 0; i < len; i += 2) { + if (modhex[i] == '\0') break; /* * We only deal with whole bytes */ - if (modhex[(i << 1) + 1] == '\0') - return -1; + if (modhex[i + 1] == '\0') return -1; - if (!(c1 = memchr(modhextab, tolower((uint8_t) modhex[i << 1]), 16)) || - !(c2 = memchr(modhextab, tolower((uint8_t) modhex[(i << 1) + 1]), 16))) + if (!(c1 = memchr(modhextab, tolower((uint8_t) modhex[i]), 16)) || + !(c2 = memchr(modhextab, tolower((uint8_t) modhex[i + 1]), 16))) return -1; hex[i] = hextab[c1 - modhextab]; @@ -124,6 +121,10 @@ static ssize_t modhex_to_hex_xlat(UNUSED void *instance, REQUEST *request, char return -1; } + if (len < (ssize_t) outlen) { + out[len] = '\0'; + } + return len; } @@ -142,7 +143,7 @@ static int mod_bootstrap(CONF_SECTION *conf, void *instance) } #endif - if (!cf_section_name2(conf)) return 0; + if (cf_section_name2(conf)) return 0; xlat_register("modhextohex", modhex_to_hex_xlat, NULL, inst); diff --git a/src/modules/stable b/src/modules/stable index 8abe0fe..d5aba18 100644 --- a/src/modules/stable +++ b/src/modules/stable @@ -6,6 +6,7 @@ rlm_counter rlm_detail rlm_dhcp rlm_digest +rlm_dpsk rlm_dynamic_clients rlm_eap rlm_exec diff --git a/src/tests/Makefile b/src/tests/Makefile index 2dab5b1..3fba18c 100644 --- a/src/tests/Makefile +++ b/src/tests/Makefile @@ -170,6 +170,7 @@ config/eap-test: $(RADDB_PATH)mods-available/eap config/eap-test-inner-tunnel -e 's/= inner-tunnel/= eap-test-inner-tunnel/;s/use_tunneled_reply = no/use_tunneled_reply = yes/' \ -e 's/enable = no/enable = yes/' \ -e 's/^\(.*\)persist_dir =/ persist_dir =/' \ + -e 's/#.*softfail =.*/softfail = yes/' \ -e 's/tls_min_version = "1.2"/tls_min_version = "1.0"/' \ -e '$(if $(TLS1_3),s/tls_max_version = "1.2"/tls_max_version = "1.3"/)' \ -e 's/cipher_list = "DEFAULT"/cipher_list = "DEFAULT${SECLEVEL}"/' \ @@ -185,6 +186,7 @@ radiusd.pid: test.conf tail -n 20 "$(TEST_PATH)/radius.log"; \ fi ${Q}echo "ok" + ${Q}echo "radiusd logging to $(TEST_PATH)/radius.log" # We can't make this depend on radiusd.pid, because then make will create # radiusd.pid when we make radiusd.kill, which we don't want. @@ -214,7 +216,23 @@ radiusd.kill: # ifneq "$(EAPOL_TEST)" "" EAP_FILES = eap-md5.conf -EAP_TLS_FILES = eap-ttls-pap.conf eap-ttls-mschapv2.conf peap-mschapv2.conf +EAP_FILES += eap-mschapv2.conf + +EAP_TLS_FILES = eap-tls.conf +EAP_TLS_FILES += eap-ttls-eap-gtc.conf +EAP_TLS_FILES += eap-ttls-eap-mschapv2.conf +EAP_TLS_FILES += eap-ttls-eap-tls.conf +EAP_TLS_FILES += eap-ttls-mschapv2.conf +EAP_TLS_FILES += eap-ttls-pap.conf +EAP_TLS_FILES += peap-client-mschapv2.conf +EAP_TLS_FILES += peap-eap-tls.conf +EAP_TLS_FILES += peap-gtc.conf +EAP_TLS_FILES += peap-mschapv2.conf + +#EAP_TLS_FILES += eap-fast.conf # disabled in default config +#EAP_TLS_FILES += eap-pwd.conf # disabled in default config +#EAP_TLS_FILES += eap-teap-mschapv2.conf # not configured in eapol_test + EAP_TLS_VERSIONS = 1.1 1.2 EAP_TLS_DISABLE_STRING = tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 @@ -313,7 +331,7 @@ EAPOL_OK_FILES := $(sort $(addprefix $(BUILD_PATH)/tests/eap/,$(patsubst %.conf, tests.eap: $(EAPOL_OK_FILES) | radiusd.kill radiusd.pid else tests.eap: - ${Q}echo "EAPOL Tests is disabled" + ${Q}echo "EAP tests are disabled" endif # we have eapol_test built # kill the server (if it's running) @@ -326,3 +344,4 @@ tests.runtests: test.conf | radiusd.kill radiusd.pid ${Q}BIN_PATH="$(BIN_PATH)" PORT="$(PORT)" ./runtests.sh $(TESTS) tests: tests.runtests tests.eap + $(MAKE) radiusd.kill diff --git a/src/tests/all.mk b/src/tests/all.mk index 142772b..678ce59 100644 --- a/src/tests/all.mk +++ b/src/tests/all.mk @@ -4,15 +4,15 @@ SECRET := testing123 DICT_PATH := $(top_srcdir)/share # -# Include all of the autoconf definitions into the Make variable space +# Pull all of the autoconf stuff into here. # --include $(BUILD_DIR)/tests/keywords/autoconf.h.mk +$(BUILD_DIR)/tests/autoconf.h.mk: src/include/autoconf.h + @grep '^#define' $^ | sed 's/#define /AC_/;s/ / := /' > $@ # -# Pull all of the autoconf stuff into here. +# Include all of the autoconf definitions into the Make variable space # -$(BUILD_DIR)/tests/keywords/autoconf.h.mk: src/include/autoconf.h - @grep '^#define' $^ | sed 's/#define /AC_/;s/ / := /' > $@ +-include $(BUILD_DIR)/tests/autoconf.h.mk ###################################################################### # diff --git a/src/tests/eap-teap-mschapv2.conf b/src/tests/eap-teap-mschapv2.conf new file mode 100644 index 0000000..4b19bef --- /dev/null +++ b/src/tests/eap-teap-mschapv2.conf @@ -0,0 +1,21 @@ +# +# eapol_test -c eap-teap-mschapv2.conf -s testing123 +# +network={ + key_mgmt=IEEE8021X + eap=TEAP + anonymous_identity="anonymous" + identity="bob" + password="bob" + +# openssl_ciphers="DEFAULT@SECLEVEL=1" +# phase1="tls_disable_tlsv1_0=1 tls_disable_tlsv1_1=1 tls_disable_tlsv1_2=1 tls_disable_tlsv1_3=0" + phase2="autheap=MSCHAPV2" + +# phase1="tls_disable_session_ticket=0 fast_provisioning=2" +# phase1="fast_provisioning=0" + pac_file="blob://eap-fast-pac" + + ca_cert="../../raddb/certs/ca.pem" +# ca_cert="/etc/freeradius/certs/ca.pem" +} diff --git a/src/tests/eap-ttls-eap-gtc.conf b/src/tests/eap-ttls-eap-gtc.conf new file mode 100644 index 0000000..2796a99 --- /dev/null +++ b/src/tests/eap-ttls-eap-gtc.conf @@ -0,0 +1,17 @@ +# +# eapol_test -c eap-ttls-eap-gtc.conf -s testing123 +# +network={ + key_mgmt=IEEE8021X + eap=TTLS + + anonymous_identity="anonymous" + + identity="bob" + password="bob" + + phase1="" + phase2="autheap=GTC" + + ca_cert="../../raddb/certs/ca.pem" +} diff --git a/src/tests/keywords/all.mk b/src/tests/keywords/all.mk index 739b738..6535d35 100644 --- a/src/tests/keywords/all.mk +++ b/src/tests/keywords/all.mk @@ -21,15 +21,6 @@ $(BUILD_DIR)/tests/keywords: @mkdir -p $@ # -# Find which input files are needed by the tests -# strip out the ones which exist -# move the filenames to the build directory. -# -BOOTSTRAP_EXISTS := $(addprefix $(DIR)/,$(addsuffix .attrs,$(KEYWORD_FILES))) -BOOTSTRAP_NEEDS := $(filter-out $(wildcard $(BOOTSTRAP_EXISTS)),$(BOOTSTRAP_EXISTS)) -BOOTSTRAP := $(subst $(DIR),$(BUILD_DIR)/tests/keywords,$(BOOTSTRAP_NEEDS)) - -# # For each file, look for precursor test. # Ensure that each test depends on its precursors. # @@ -49,16 +40,34 @@ $(BUILD_DIR)/tests/keywords/depends.mk: $(addprefix $(DIR)/,$(KEYWORD_FILES)) | done # -# These ones get copied over from the default input +# For sheer laziness, allow "make test.keywords.foo" # -$(BOOTSTRAP): $(DIR)/default-input.attrs | $(BUILD_DIR)/tests/keywords - @cp $< $@ +define KEYWORD_TEST +tests.keywords.${1}: $(addprefix $(OUTPUT)/,${1}) + +tests.keywords.help: TEST_KEYWORDS_HELP += tests.keywords.${1} + +OUTPUT := $(BUILD_DIR)/tests/keywords # -# These ones get copied over from their original files +# Create the input attrs, either from the test-specific input, +# or from the default input. # -$(BUILD_DIR)/tests/keywords/%.attrs: $(DIR)/%.attrs | $(BUILD_DIR)/tests/keywords - @cp $< $@ +$(OUTPUT)/${1}: $(OUTPUT)/${1}.attrs | $(dir $(OUTPUT)/${1}) +$(OUTPUT)/${1}.attrs: | $(dir $(OUTPUT)/${1}) + +ifneq "$(wildcard src/tests/keywords/${1}.attrs)" "" +$(OUTPUT)/${1}.attrs: src/tests/keywords/${1}.attrs +else +$(OUTPUT)/${1}.attrs: src/tests/keywords/default-input.attrs +endif + @cp $$< $$@ +ifeq "${1}" "mschap" +$(OUTPUT)/${1}: rlm_mschap.la +endif + +endef +$(foreach x,$(KEYWORD_FILES),$(eval $(call KEYWORD_TEST,$x))) # # Don't auto-remove the files copied by the rule just above. diff --git a/src/tests/keywords/randstr b/src/tests/keywords/randstr new file mode 100644 index 0000000..6884cb0 --- /dev/null +++ b/src/tests/keywords/randstr @@ -0,0 +1,16 @@ +update request { + &Tmp-Octets-0 := "0x%{randstr:16h}" + &Tmp-String-0 := "%{randstr:16h}" +} + +if ("%{length:Tmp-Octets-0}" == 16) { + update reply { + Filter-Id := "filter" + } +} + +if ("%{length:Tmp-String-0}" == 32) { + update reply { + Filter-Id := "filter" + } +} diff --git a/src/tests/modules/date/all.mk b/src/tests/modules/date/all.mk new file mode 100644 index 0000000..90966df --- /dev/null +++ b/src/tests/modules/date/all.mk @@ -0,0 +1,3 @@ +# +# Test the "date" module +# diff --git a/src/tests/modules/date/date_xlat.attrs b/src/tests/modules/date/date_xlat.attrs new file mode 100644 index 0000000..ba430d2 --- /dev/null +++ b/src/tests/modules/date/date_xlat.attrs @@ -0,0 +1,13 @@ +# +# Input packet +# +Packet-Type = Access-Request +User-Name = 'Bob' +User-Password = 'Alice' +Tmp-Integer-6 = 0 + +# +# Expected answer +# +Response-Packet-Type == Access-Accept + diff --git a/src/tests/modules/date/date_xlat.unlang b/src/tests/modules/date/date_xlat.unlang new file mode 100644 index 0000000..c5501dd --- /dev/null +++ b/src/tests/modules/date/date_xlat.unlang @@ -0,0 +1,243 @@ +# +# Selection of tests for the %{time_since:} xlat +# +# Somewhat limited in what we can do here, as it bases its +# responses off the current system time. So we need to do some +# comparisons rather than actual value checks. +# + +# +# %{time_since:...} should never return 0 +# +update { + &Tmp-Integer64-0 := "%{time_since:s}" + &Tmp-Integer64-1 := "%{time_since:ms}" + &Tmp-Integer64-2 := "%{time_since:us}" +} + +if (&Tmp-Integer64-0 == 0 || &Tmp-Integer64-1 == 0 || &Tmp-Integer64-2 == 0) { + test_fail +} + +# +# and they should all be different +# +if (&Tmp-Integer64-0 == &Tmp-Integer64-1 || \ + &Tmp-Integer64-1 == &Tmp-Integer64-2 || \ + &Tmp-Integer64-2 == &Tmp-Integer64-0) { + test_fail +} + +# +# %c and %{time_since:s:0} should match +# +update { + &Tmp-Integer-9 := 0 +} + +update { + &Tmp-Integer-0 := "%c" + &Tmp-Integer-1 := "%{time_since:s 0}" + &Tmp-Integer-2 := "%{time_since:s &Tmp-Integer-9}" +} + +if (&Tmp-Integer-0 != &Tmp-Integer-1) { + if (&Tmp-Integer-0 != "%{expr:&Tmp-Integer-1 - 1}") { + # at a push, %{time_since:s 0} might be one second later, + # depending on when the test ran + test_fail + } +} + +if (&Tmp-Integer-1 != &Tmp-Integer-2) { + if (&Tmp-Integer-1 != "%{expr:&Tmp-Integer-2 - 1}") { + test_fail + } +} + +# +# If we run time_since 3 times, they should be the same or increasing +# +update { + &Tmp-Integer64-0 := "%{time_since:s 0}" +} + +update { + &Tmp-Integer64-1 := "%{time_since:s }" +} + +update { + &Tmp-Integer64-2 := "%{time_since:s}" +} + +if (&Tmp-Integer64-0 > &Tmp-Integer64-1 || \ + &Tmp-Integer64-1 > &Tmp-Integer64-2 || \ + &Tmp-Integer64-0 > &Tmp-Integer64-2) { + test_fail +} + +# +# It's way past the year 2020, so this should only fail if the +# computer's clock is very wrong... +# +if (&Tmp-Integer64-0 < 1600000000) { + test_fail +} + + +# +# Similar for milliseconds +# +update { + &Tmp-Integer64-3 := "%{time_since:ms &request:Tmp-Integer-6}" +} + +update { + &Tmp-Integer64-4 := "%{time_since:ms}" +} + +update { + &Tmp-Integer64-5 := "%{time_since:ms &Tmp-Integer-9}" +} + +if (&Tmp-Integer64-3 > &Tmp-Integer64-4 || \ + &Tmp-Integer64-4 > &Tmp-Integer64-5 || \ + &Tmp-Integer64-3 > &Tmp-Integer64-5) { + test_fail +} + + +# +# ...and microseconds +# +update session-state { + &Tmp-Integer-7 := 0 +} + +update { + &Tmp-Integer64-6 := "%{time_since:us &session-state:Tmp-Integer-7 }" +} + +update { + &Tmp-Integer64-7 := "%{time_since:us }" +} + +update { + &Tmp-Integer64-8 := "%{time_since:us}" +} + +if (&Tmp-Integer64-6 > &Tmp-Integer64-7 || \ + &Tmp-Integer64-7 > &Tmp-Integer64-8 || \ + &Tmp-Integer64-6 > &Tmp-Integer64-8) { + test_fail +} + +if ("%{expr:&Tmp-Integer64-7 - &Tmp-Integer64-6}" > 250) { + # you have a really slow computer if the time between + # getting these took more than 250us + test_fail +} + + +# +# Seconds component * 1000 must always be same or less than +# milliseconds, and microseconds. +# +if ("%{expr:%{time_since:s 0} * 1000}" > "%{time_since:ms 0}") { + test_fail +} + +if ("%{expr:%{time_since:ms 0} * 1000}" > "%{time_since:us 0}") { + test_fail +} + +if ("%{expr:%{time_since:s 0} * 1000000}" > "%{time_since:us 0}") { + test_fail +} + + +# +# Test for some errors +# + +# missing time base +update { + &Tmp-Integer-0 := "%{time_since:}" +} + +if (!(&Module-Failure-Message[*] == 'Time base (ms, us, s) missing in time_since xlat')) { + test_fail +} + +update { + &Module-Failure-Message !* ANY +} + + +# invalid time base +update { + &Tmp-Integer-0 := "%{time_since:bob}" +} + +if (!(&Module-Failure-Message[*] == 'Time base (ms, us, s) missing in time_since xlat')) { + test_fail +} + +update { + &Module-Failure-Message !* ANY +} + + +# negative values +update { + &Tmp-Integer-0 := "%{time_since:ms -1234}" +} + +if (!(&Module-Failure-Message[*] == 'time_since xlat only accepts positive integers')) { + test_fail +} + +update { + &Module-Failure-Message !* ANY +} + + +# invalid attribute +update { + &Tmp-Integer-0 := "%{time_since:us &Test-Non-Existant-Attr}" +} + +if (!(&Module-Failure-Message[*] == 'Unable to parse attribute in time_since xlat')) { + test_fail +} + +update { + &Module-Failure-Message !* ANY +} + + +# silly text +update { + &Tmp-Integer-0 := "%{time_since:us test random text}" +} + +if (!(&Module-Failure-Message[*] == 'Failed parsing "test random text" as integer')) { + test_fail +} + +update { + &Module-Failure-Message !* ANY +} + + +# attribute not in list (warning, so check output) +update { + &Tmp-Integer-0 := "%{time_since:us &reply:Tmp-Integer-4}" +} + +if (&Tmp-Integer-0 != 0) { + test_fail +} + + +test_pass diff --git a/src/tests/modules/date/module.conf b/src/tests/modules/date/module.conf new file mode 100644 index 0000000..cb7ef07 --- /dev/null +++ b/src/tests/modules/date/module.conf @@ -0,0 +1,3 @@ +#date unit test config +date { +} diff --git a/src/tests/modules/dpsk/pmk.txt b/src/tests/modules/dpsk/pmk.txt new file mode 100644 index 0000000..db3b6bd --- /dev/null +++ b/src/tests/modules/dpsk/pmk.txt @@ -0,0 +1,7 @@ +User-Name = "cae78dfa6504" +User-Password = "cae78dfa6504" +Called-Station-Id = "5c:df:89:11L3bL3c:SSID" +Calling-Station-Id = "ca:e7:8d:fa:65:04" +FreeRADIUS-802.1X-Anonce = 0x43426fd6469d4254eb0d5ba449eb9895360894f1948cece9196751336d4c5daf +FreeRADIUS-802.1X-EAPoL-Key-Msg = 0x0103007502010a00000000000000000001b16a8514b84d7843e53754f5c9131cb203fbe8277dbf216d6e87fd6e30b0577a0000000000000000000000000000000000000000000000000000000000000000dc81aec5a05ee8aa21a52947041fd2fc001630140100000fac040100000fac040100000fac028000 +Class = 0xd6175aed517504c40b8831d7ce7b7d1fe24c65ce0f92c2816ca14ba7acb47b13 diff --git a/src/tests/modules/dpsk/psk.txt b/src/tests/modules/dpsk/psk.txt new file mode 100644 index 0000000..f4e584f --- /dev/null +++ b/src/tests/modules/dpsk/psk.txt @@ -0,0 +1,9 @@ +User-Name = "8ab3a0ebd5e5" +User-Password = "8ab3a0ebd5e5" +NAS-IP-Address = 127.0.0.1 +Called-Station-Id = "34:ef:b6:af:48:9e:Andrena_39_Lincoln" +Calling-Station-Id = "8a:b3:a0:eb:d5:e5" +NAS-Identifier = "34efb6af489e" +FreeRADIUS-802.1X-Anonce = 0x4df70a4285c5c61f177cdbfc29d7e3cac94167f6101f1bcab420dd50c4f8809d +FreeRADIUS-802.1X-EAPoL-Key-Msg = 0x0203007502010a00100000000000000001c3bb319516614aacfb44e933bf1671131fb1856e5b2721952d414ce3f5aa312b000000000000000000000000000000000000000000000000000000000000000035cddcedad0dfb6a12a2eca55c17c323001630140100000fac040100000fac040100000fac028c00 +Filter-ID = "Pancakes1124" diff --git a/src/tests/modules/dpsk/radiusd.conf b/src/tests/modules/dpsk/radiusd.conf new file mode 100644 index 0000000..c4d1782 --- /dev/null +++ b/src/tests/modules/dpsk/radiusd.conf @@ -0,0 +1,15 @@ + rewrite_called_station_id + dpsk + if (ok) { + if (&Class) { + update control { + &Pairwise-Master-Key := &Class + } + } + elsif (&Filter-ID) { + update control { + &Pre-Shared-Key := &Filter-ID + } + } + } + diff --git a/src/tests/modules/files/authorize b/src/tests/modules/files/authorize index b85f6a2..6ef314e 100644 --- a/src/tests/modules/files/authorize +++ b/src/tests/modules/files/authorize @@ -90,3 +90,13 @@ addcontrol Cleartext-Password := "testing123", Reply-Message := "success1" Fall-Through = yes addcontrol Reply-Message += "success2" + + +# +# Doesn't match +# +DEFAULT Framed-IP-Address == 192.0.2.1 + Reply-Message += "unexpected match in DEFAULT" + +DEFAULT + Reply-Message = "empty DEFAULT" diff --git a/src/tests/modules/files/empty_default.attrs b/src/tests/modules/files/empty_default.attrs new file mode 100644 index 0000000..428fa1e --- /dev/null +++ b/src/tests/modules/files/empty_default.attrs @@ -0,0 +1,11 @@ +# +# Input packet +# +User-Name = "empty_default" +User-Password = "testing123" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept +Reply-Message == "empty DEFAULT" diff --git a/src/tests/modules/files/empty_default.unlang b/src/tests/modules/files/empty_default.unlang new file mode 100644 index 0000000..ac4aa4d --- /dev/null +++ b/src/tests/modules/files/empty_default.unlang @@ -0,0 +1,9 @@ +# +# Run the "files" module +# +files + +update control { + Auth-Type := Accept +} + diff --git a/src/tests/modules/yubikey/all.mk b/src/tests/modules/yubikey/all.mk new file mode 100644 index 0000000..b62dbc2 --- /dev/null +++ b/src/tests/modules/yubikey/all.mk @@ -0,0 +1,3 @@ +# +# Test the "yubikey" module xlat +# diff --git a/src/tests/modules/yubikey/module.conf b/src/tests/modules/yubikey/module.conf new file mode 100644 index 0000000..a9549f3 --- /dev/null +++ b/src/tests/modules/yubikey/module.conf @@ -0,0 +1,11 @@ +yubikey { + + id_length = 12 + + split = yes + + decrypt = yes + + validate = no + +} diff --git a/src/tests/modules/yubikey/yubikey_auth.attrs b/src/tests/modules/yubikey/yubikey_auth.attrs new file mode 100644 index 0000000..d1fa1de --- /dev/null +++ b/src/tests/modules/yubikey/yubikey_auth.attrs @@ -0,0 +1,11 @@ +# +# Input packet +# +Packet-Type = Access-Request +User-Name = "bob" +User-Password = "helloddddgciilcjkjhlifidginuirlhgidcvbfnutjnibldi" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept diff --git a/src/tests/modules/yubikey/yubikey_auth.unlang b/src/tests/modules/yubikey/yubikey_auth.unlang new file mode 100644 index 0000000..ae9f534 --- /dev/null +++ b/src/tests/modules/yubikey/yubikey_auth.unlang @@ -0,0 +1,56 @@ +# Call yubikey module to split OTP from password +yubikey + +if !(&User-Password == 'hello') { + test_fail +} +if !(&Yubikey-OTP) { + test_fail +} +if !(&Yubikey-Public-Id == 'ddddgciilcjk') { + test_fail +} + +update control { + &Yubikey-Counter := 1 + &Yubikey-Key := 0xb8c56af07ff79b2230e04ab8891784ce +} + +# Call module in authenticate mode to decrypt OTP +yubikey.authenticate + +# Check all the attributes have been created +if !(&Yubikey-Private-Id == 0x1dfc67f97828) { + test_fail +} +if !(&Yubikey-Timestamp) { + test_fail +} +if !(&Yubikey-Counter == 258) { + test_fail +} +if !(&Yubikey-Random) { + test_fail +} + + +# Increase the known "counter" value to detect a replay attack +update { + &control:Yubikey-Counter := &Yubikey-Counter +} + +yubikey.authenticate { + reject = 1 +} + +# Replay attack should result in a reject and a suitable module failure +if !(reject) { + test_fail +} +debug_all + +if !(&Module-Failure-Message == 'yubikey: Replay attack detected! Counter value 258, is lt or eq to last known counter value 258') { + test_fail +} + +test_pass diff --git a/src/tests/modules/yubikey/yubikey_xlat.attrs b/src/tests/modules/yubikey/yubikey_xlat.attrs new file mode 100644 index 0000000..1cce1c5 --- /dev/null +++ b/src/tests/modules/yubikey/yubikey_xlat.attrs @@ -0,0 +1,11 @@ +# +# Input packet +# +Packet-Type = Access-Request +User-Name = "bob" +User-Password = "hello" + +# +# Expected answer +# +Response-Packet-Type == Access-Accept diff --git a/src/tests/modules/yubikey/yubikey_xlat.unlang b/src/tests/modules/yubikey/yubikey_xlat.unlang new file mode 100644 index 0000000..bc17642 --- /dev/null +++ b/src/tests/modules/yubikey/yubikey_xlat.unlang @@ -0,0 +1,42 @@ +update { + &Tmp-String-0 := 'vvrbuctetdhc' + &Tmp-String-1 := "%{modhextohex:%{Tmp-String-0}}" +} + +if (&Tmp-String-1 != 'ffc1e0d3d260') { + test_fail +} + +# Invalid modhex string - not even length +update { + &Tmp-String-0 := 'vvrbuctetdh' + &Tmp-String-1 := "%{modhextohex:%{Tmp-String-0}}" +} + +if (ok) { + test_fail +} + +if (&Tmp-String-1 != "") { + test_fail +} + +if (&Module-Failure-Message != "Modhex string invalid") { + test_fail +} + +# Invalid modhex string - invalid characters +update { + &Tmp-String-0 := 'vxrbmctetdhc' + &Tmp-String-1 := "%{modhextohex:%{Tmp-String-0}}" +} + +if (ok) { + test_fail +} + +if (&Tmp-String-1 != "") { + test_fail +} + +test_pass diff --git a/src/tests/peap-gtc.conf b/src/tests/peap-gtc.conf new file mode 100644 index 0000000..bc8c74b --- /dev/null +++ b/src/tests/peap-gtc.conf @@ -0,0 +1,13 @@ +# +# ./eapol_test -c peap-gtc.conf -s testing123 +# +network={ + ssid="example" + key_mgmt=WPA-EAP + eap=PEAP + identity="bob" + anonymous_identity="anonymous" + password="bob" + phase1="" + phase2="auth=GTC" +} diff --git a/src/tests/unit/vendor.txt b/src/tests/unit/vendor.txt index 1325f49..088bd1b 100644 --- a/src/tests/unit/vendor.txt +++ b/src/tests/unit/vendor.txt @@ -46,3 +46,9 @@ original null encode ERX-LI-Action = off decode - data ERX-LI-Action = off + +encode Aruba-MPSK-Lookup-Info = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" +data 1a c8 00 00 39 e7 42 c2 c5 6f 16 e5 de 2d 2a 2d d3 0e ac 92 12 c5 97 af 8e 08 f0 92 b4 45 4d 24 5d 73 16 a8 5a cd 78 0a f2 5e 7f e5 e1 fe 95 79 ee 2e 5b 0e ac bf fd 8c 15 da 9c 59 1d 53 5b 76 49 e9 71 4d d7 00 1c 04 65 51 cb 35 66 81 36 0d 25 ab 23 3b 67 5a 30 f8 0d 66 2b bf 97 f5 18 03 34 79 7a 22 11 c1 02 78 94 b0 26 62 13 4a c1 9c 77 6f b8 7c 29 ee 8b 61 14 de 90 b6 94 3f d0 01 00 57 6d 48 2a 59 f3 d4 57 d2 04 af 4e 64 0b 11 31 9e 63 49 f3 fa 61 4d c9 38 88 d1 89 3f 2a 10 d3 8f a0 5d 46 5f 0a b1 2f 9a 70 fa 35 79 c7 a6 68 69 28 98 49 d5 7a 29 9d dc 3d 2f 43 52 f5 12 b3 bf 61 80 2e 7a 3a 0c + +decode - +data Aruba-MPSK-Lookup-Info = "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" diff --git a/suse/freeradius.spec b/suse/freeradius.spec index 893d4fe..139fcd8 100644 --- a/suse/freeradius.spec +++ b/suse/freeradius.spec @@ -1,5 +1,5 @@ Name: freeradius-server -Version: 3.2.3 +Version: 3.2.5 Release: 0 License: GPLv2 ; LGPLv2.1 Group: Productivity/Networking/Radius/Servers |