Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • fornari/ngx_http_voms_module
  • cnafsd/ngx_http_voms_module
2 results
Show changes
Commits on Source (349)
Showing
with 1055 additions and 117 deletions
# Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
# SPDX-License-Identifier: EUPL-1.2
FROM almalinux:9
# Allow customization of build user ID and name
ARG USERNAME=vscode
ARG USER_UID=1000
ARG USER_GID=${USER_UID}
COPY library-scripts/*.sh /tmp/library-scripts/
RUN \
sh /tmp/library-scripts/provide-dev-deps.sh && \
sh /tmp/library-scripts/provide-user.sh ${USERNAME} ${USER_UID} ${USER_GID} && \
dnf clean all && rm -rf /var/cache/dnf
USER $USERNAME
# `ngx_http_voms_module` for developers
A devcontainer is ready to use for the developers. A set of packages without nginx are already installed.
## How to build and install nginx with or without httpg patch
To build and install the latest stable version of [nginx](http://nginx.org/en/download.html) you have to copy the `nginx.repo` file (it is contained in the `docker` directory) into the `/etc/yum.repos.d/` directory and install nginx with `yum`:
```shell
$ sudo cp docker/nginx.repo /etc/yum.repos.d/
$ sudo yum install -y nginx
```
Otherwise, if you want to build and install the latest stable version of [nginx](http://nginx.org/en/download.html) with the httpg patch, a bash library is ready to use. You can source it and follow the commands below:
```shell
$ source .devcontainer/assets/build-library.sh
$ downloadNginx
$ buildHttpgNginxRPM
$ sudo rpm -ivh ~/rpmbuild/RPMS/x86_64/nginx-*.httpg.x86_64.rpm
```
## How to build and install the `ngx_http_voms_module`
If you want to build and install the `ngx_http_voms_module`, nginx have to be installed in the container (see the previous section). When this requirement is satisfied, you can use the library contained in the `.devcontainer/assets` folder as follows (NOTE: if you have already download nginx source file, you can skip the relative command):
```shell
$ source .devcontainer/assets/build-library.sh
$ downloadNginx
$ buildVomsModuleRPM
$ sudo rpm -ivh ~/rpmbuild/RPMS/x86_64/nginx-module-http-voms-*.x86_64.rpm
```
## How to manage this project
If you want to understand how this project works, start from the CI. Three stages are defined:
### 1. build-rpms
Starting from a clear AlmaLinux 9, we install all the useful packages to compile nginx and to build a rpm package. The bash steps that achieves these results are defined in the `.devcontainer/assets/build-library.sh` file, so you can read that bash script to learn which nginx version we use, how to download it, how to set up the environment and how to build the rpm.
It is important to underline that to build the nginx rpm we use the spec file in the `rpm` repo, that is the official nginx 1.24.0 spec file increased by the HTTPG patch. To build the `ngx_http_voms_module` we have defined an appropriate spec file indeed. The files that are used to build the rpm module are written, called and collocated following the common practices of the nginx modules: a source file is defined in the `src` folder, the `config` and the `config.make` files are in the root project directory.
At the end of this stage, all the useful rpms are saved as job artifacts.
### 2. docker-build-rpms
In this stage we set up a docker image with nginx, the httpg patch and the `ngx_http_voms_module`. To do this, we use a set of scripts in the [`helper-scripts`](https://baltig.infn.it/mw-devel/helper-scripts.git) project.
The dockerfile and all the files needed for its compilation are in the `docker` directory. The image starts from AlmaLinux 9, defines a user and installs a set of useful packages. After that we import the nginx repo file, in this way we can download a lot of packages provided by nginx, including its last stable version. In the end we install the rpm packages that we build in the previous stage and the njs module.
### 3. push-to-dockerhub
In this last stage we push on dockerhub the image that we have builded in the previous stage. Note that this stage is run only when we push something in the master branch.
#!/usr/bin/env bash
# Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
# SPDX-License-Identifier: EUPL-1.2
downloadNginx() {
# check if ~/rpmbuild exists and is not empty
if [ -d ${HOME}/rpmbuild ] && [ "$(ls -A ${HOME}/rpmbuild)" ]; then
>&2 echo "Error: ${HOME}/rpmbuild already exists and is not empty"
return 1
fi
# set nginx version
if [ -z ${ngxVersion} ]; then
ngxVersion=1.26.2-1
fi
elVersion=$(rpmbuild --eval %{rhel})
echo "Downloading nginx version ${ngxVersion} (EL${elVersion})"
src_package_name="nginx-${ngxVersion}.el${elVersion}.ngx.src.rpm"
src_package_url="https://nginx.org/packages/centos/${elVersion}/SRPMS/${src_package_name}"
wget ${src_package_url}
rpm -i ${src_package_name}
}
buildHttpgNginxRPM() {
if ! printenv CI_PROJECT_DIR > /dev/null; then
>&2 echo "CI_PROJECT_DIR is not set in the environment, assuming the current working directory '${PWD}'"
export CI_PROJECT_DIR="${PWD}"
fi
sh ${CI_PROJECT_DIR}/rpm/addPatchToNginxSpec.sh
# build rpm
rpmlint ~/rpmbuild/SPECS/nginx.spec
rpmbuild -ba ~/rpmbuild/SPECS/nginx.spec
}
buildVomsModuleRPM() {
if [ -z ${CI_PROJECT_DIR} ]; then
CI_PROJECT_DIR="/workspaces/ngx_http_voms_module"
fi
# set voms modules sources
cd ~/rpmbuild/SOURCES/
mkdir ngx-http-voms-module
cp ${CI_PROJECT_DIR}/config ngx-http-voms-module/
cp ${CI_PROJECT_DIR}/config.make ngx-http-voms-module/
cp -r ${CI_PROJECT_DIR}/src ngx-http-voms-module/
cp ${CI_PROJECT_DIR}/rpm/nginx-module-http-voms.spec ~/rpmbuild/SPECS
# build rpm
rpmlint ~/rpmbuild/SPECS/nginx-module-http-voms.spec
rpmbuild -ba ~/rpmbuild/SPECS/nginx-module-http-voms.spec
cd ${CI_PROJECT_DIR}
}
// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
// https://github.com/microsoft/vscode-dev-containers/tree/v0.159.0/containers/cpp
{
"name": "C++",
"build": {
"dockerfile": "Dockerfile",
},
"runArgs": [
"--cap-add=SYS_PTRACE",
"--security-opt",
"seccomp=unconfined"
],
// Set *default* container specific settings.json values on container create.
"settings": {
"terminal.integrated.defaultProfile.linux": "bash"
},
// Add the IDs of extensions you want installed when the container is created.
"extensions": [
"ms-vscode.cpptools",
],
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
//"postCreateCommand": "sudo debuginfo-install -y voms",
// Comment out this line to run as root instead.
"remoteUser": "vscode",
"remoteEnv": {"NGX_HTTP_VOMS_MODULE_ROOT": "${containerWorkspaceFolder}"}
}
\ No newline at end of file
#!/usr/bin/env bash
# Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
# SPDX-License-Identifier: EUPL-1.2
set -ex
dnf install -y epel-release
dnf update -y
dnf install -y --setopt=tsflags=nodocs \
which \
wget \
sudo \
file \
git \
gcc-c++ \
gd-devel \
gettext \
ccache \
libxslt-devel \
lcov \
perl-ExtUtils-Embed \
perl-Digest-SHA \
readline-devel \
boost-devel \
voms-devel \
make \
patch \
zlib-devel \
pcre2-devel \
rpmdevtools \
rpmlint \
cpan \
voms-clients-cpp
\ No newline at end of file
#!/usr/bin/env bash
# Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
# SPDX-License-Identifier: EUPL-1.2
USERNAME=${1}
USER_UID=${2}
USER_GID=${3}
set -e
if [ "$(id -u)" -ne 0 ]; then
echo -e 'Script must be run as root. Use sudo, su, or add "USER root" to your Dockerfile before running this script.'
exit 1
fi
groupadd --gid $USER_GID $USERNAME
useradd --uid $USER_UID --gid $USER_GID -m $USERNAME
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME
chmod 0440 /etc/sudoers.d/$USERNAME
CODESPACES_BASH="$(cat \
<<'EOF'
# Codespaces bash prompt theme
__bash_prompt() {
local userpart='`export XIT=$? \
&& [ ! -z "${GITHUB_USER}" ] && echo -n "\[\033[0;32m\]@${GITHUB_USER} " || echo -n "\[\033[0;32m\]\u " \
&& [ "$XIT" -ne "0" ] && echo -n "\[\033[1;31m\]➜" || echo -n "\[\033[0m\]➜"`'
local gitbranch='`\
export BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null); \
if [ "${BRANCH}" = "HEAD" ]; then \
export BRANCH=$(git describe --contains --all HEAD 2>/dev/null); \
fi; \
if [ "${BRANCH}" != "" ]; then \
echo -n "\[\033[0;36m\](\[\033[1;31m\]${BRANCH}" \
&& if git ls-files --error-unmatch -m --directory --no-empty-directory -o --exclude-standard ":/*" > /dev/null 2>&1; then \
echo -n " \[\033[1;33m\]✗"; \
fi \
&& echo -n "\[\033[0;36m\]) "; \
fi`'
local lightblue='\[\033[1;34m\]'
local removecolor='\[\033[0m\]'
PS1="${userpart} ${lightblue}\w ${gitbranch}${removecolor}\$ "
unset -f __bash_prompt
}
__bash_prompt
EOF
)"
USER_RC_PATH="/home/${USERNAME}"
echo "${CODESPACES_BASH}" >> "${USER_RC_PATH}/.bashrc"
chown ${USERNAME}:${USER_GID} "${USER_RC_PATH}/.bashrc"
echo "Done!"
.vscode .vscode
servroot* servroot*
nginx nginx
\ No newline at end of file docker/artifacts
node_modules
t/*
!t/README.md
!t/*.t
!t/setup.sh
!t/conf.d
!t/proxies.d
!t/openssl.conf
!t/socket.js
stages:
- build
- docker-build
- docker-push
build-rpms-el8:
stage: build
image: almalinux:8
script:
- env | sort
- dnf -y install epel-release
- dnf install -y wget openssl-devel zlib-devel pcre2-devel make rpmdevtools rpmlint boost-devel voms-devel gcc-c++
- source .devcontainer/assets/build-library.sh
- CI_PROJECT_DIR=$PWD
- downloadNginx
- buildHttpgNginxRPM
- buildVomsModuleRPM
- cd ${CI_PROJECT_DIR}/docker && mkdir artifacts
- cp ~/rpmbuild/SRPMS/* artifacts/
- cp ~/rpmbuild/RPMS/x86_64/* artifacts/
artifacts:
paths:
- docker/artifacts/
build-rpms-el9:
stage: build
image: almalinux:9
script:
- env | sort
- dnf -y install epel-release
- dnf install -y wget openssl-devel zlib-devel pcre2-devel make rpmdevtools rpmlint boost-devel voms-devel gcc-c++
- source .devcontainer/assets/build-library.sh
- CI_PROJECT_DIR=$PWD
- downloadNginx
- buildHttpgNginxRPM
- buildVomsModuleRPM
- cd ${CI_PROJECT_DIR}/docker && mkdir artifacts
- cp ~/rpmbuild/SRPMS/* artifacts/
- cp ~/rpmbuild/RPMS/x86_64/* artifacts/
artifacts:
paths:
- docker/artifacts/
docker-build-rpms:
stage: docker-build
image: docker:latest
services:
- name: docker:dind
command: ["--tls=false"]
dependencies:
- build-rpms-el8
- build-rpms-el9
script:
- apk add git bash
- git clone https://baltig.infn.it/mw-devel/helper-scripts.git helper-scripts
- cp helper-scripts/scripts/* /usr/local/bin
- rm ${CI_PROJECT_DIR}/docker/artifacts/*-debuginfo*.rpm
- docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
- export DOCKER_REGISTRY_HOST=${CI_REGISTRY}
- export DOCKER_REGISTRY_NAMESPACE=${CI_PROJECT_PATH}
- cd docker && build-docker-image.sh && push-docker-image.sh
push-to-dockerhub:
stage: docker-push
image: docker:latest
services:
- name: docker:dind
command: ["--tls=false"]
dependencies:
- docker-build-rpms
script:
- apk add git bash
- git clone https://baltig.infn.it/mw-devel/helper-scripts.git helper-scripts
- cp helper-scripts/scripts/* /usr/local/bin
- export DOCKER_PUSH_TO_DOCKERHUB=y
- env | sort
- docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY}
- export DOCKER_REGISTRY_HOST=${CI_REGISTRY}
- export DOCKER_REGISTRY_NAMESPACE=${CI_PROJECT_PATH}
- cd docker && pull-docker-image.sh && unset DOCKER_REGISTRY_HOST
- docker login -u ${DOCKERHUB_USER} -p ${DOCKERHUB_PASSWORD} && push-docker-image.sh
only:
- master
# ngx_http_voms_module # `ngx_http_voms_module`
[![pipeline status](https://baltig.infn.it/cnafsd/ngx_http_voms_module/badges/master/pipeline.svg)](https://baltig.infn.it/cnafsd/ngx_http_voms_module/commits/master)
## Description ## Description
_ngx_http_voms_module_ is a module for the [NGINX web server](https://www.nginx.org/) that enables client-side authentication based on X.509 proxies augmented with Attribute Certificates, typically obtained through a [Virtual Organization Membership Service](https://italiangrid.github.io/voms/) (VOMS). `ngx_http_voms_module` is a module for the [Nginx web server](https://www.nginx.org/) that enables client-side authentication based on X.509 proxy certificates augmented with VOMS Attribute Certificates, typically obtained from a [Virtual Organization Membership Service](https://italiangrid.github.io/voms/) (VOMS) server.
## Installation The module defines a set of *embedded* variables, whose values are extracted from the first Attribute Certificate found in the certificate chain.
The generic installation instructions are: ### Embedded Variables
The module makes the following embedded variables available for use in an Nginx configuration file:
#### voms_user
The Subject of the End-Entity certificate, used to sign the proxy.
_Example_: ``/C=IT/O=IGI/CN=test0``
#### ssl_client_ee_s_dn
Like `voms_user`, the Subject of the End-Entity certificate. Unlike `voms_user`, it is available even for non-VOMS proxies and is formatted according to RFC 2253.
_Example_: `CN=test0,O=IGI,C=IT`
#### voms_user_ca
The Issuer (Certificate Authority) of the End-Entity certificate.
_Example_: `/C=IT/O=IGI/CN=Test CA`
#### ssl_client_ee_i_dn
$ cd nginx-1.x.y Like `voms_user_ca`, the Issuer of the End-Entity certificate. Unlike `voms_user_ca`, it is available even for non-VOMS proxies and is formatted according to RFC 2253.
$ ./configure --add-module=/path/to/ngx_http_voms_module
$ make && make install
A Docker image is available for use in the context of the StoRM2 project, where the OpenResty distribution is used: _Example_: `CN=Test CA,O=IGI,C=IT`
$ docker run --rm -it -v /path/to/ngx_http_voms_module:/home/build/ngx_http_voms_module storm2/ngx-voms-build #### voms_fqans
% cd openresty-1.x.y
% ./configure ${resty_config_options} --add-module=../ngx_http_voms_module
% make && make install
## Variables A comma-separated list of Fully Qualified Attribute Names. See [The VOMS Attribute Certificate Format](http://ogf.org/documents/GFD.182.pdf) for more details.
The module makes the following variables available for use in an NGINX configuration file: _Example_: `/test.vo/exp1,/test.vo/exp2,/test.vo/exp3/Role=PIPPO`
### voms_fqans #### voms_server
A comma-separated list of _Fully Qualified Attribute Names_ The Subject of the VOMS server certificate, used to sign the Attribute Certificate.
### voms_user _Example_: `/C=IT/O=IGI/CN=voms.example`
user DN #### voms_server_ca
### voms_user_ca The Issuer (Certificate Authority) of the VOMS server certificate.
user CA _Example_: `/C=IT/O=IGI/CN=Test CA`
### voms_server #### voms_vo
server DN The name of the Virtual Organization (VO) to which the End Entity belongs.
### voms_server_ca _Example_: `test.vo`
server CA #### voms_server_uri
### voms_vo The hostname and port of the VOMS network service that issued the Attribute Certificate, in the form _hostname_ :_port_.
VO name _Example_: `voms.example:15000`
### voms_server_uri #### voms_not_before
VOMS server URI The date before which the Attribute Certificate is not yet valid, in the form _YYYYMMDDhhmmss_ `Z`.
### voms_not_before _Example_: `20180101000000Z`
not-before #### voms_not_after
### voms_not_after The date after which the Attribute Certificate is not valid anymore, in the form _YYYYMMDDhhmmss_ `Z`.
not-after _Example_: `20180101120000Z`
#### voms_generic_attributes
A comma-separated list of attributes, each defined by three properties and formatted as `n=`_name_ `v=`_value_ `q=`_qualifier_. The qualifier typically coincides with the name of the VO.
_Example_: `n=nickname v=newland q=test.vo,n=nickname v=giaco q=test.vo`
#### voms_serial
The serial number of the Attribute Certificate in hexadecimal format.
_Example_: `7B`
## Installation
### prerequisites
The software dependecies are listed in the [provide-deps](docker/library-scripts/provide-deps.sh) script in the `docker` directory.
The nginx source files are also needed. To download them in ```/tmp/nginx-x.y.z``` you can execute:
```shell
$ ngxVersion=<version>
$ wget -O /tmp/nginx-$ngxVersion.tar.gz https://nginx.org/download/nginx-$ngxVersion.tar.gz
$ cd /tmp && tar -xzvf /tmp/nginx-$ngxVersion.tar.gz && cd -
```
### Generic installation
The generic installation instructions are:
### voms_generic_attributes ```shell
$ cd /tmp/nginx-x.y.z
$ ./configure --add-module=/path/to/ngx_http_voms_module
$ make && make install
```
generic attributes ### Docker container
### voms_serial A Docker image with nginx and the `ngx_http_voms_module` is available, you can find it [here](https://hub.docker.com/r/cnafsd/nginx-httpg-voms).
serial number ### For the developers
A [.devcontainer](.devcontainer) is provided for the developers with all the instructions on how to use it, how to build the rpm module and how to install it.
## Testing ## Testing
Setup and files to test the *ngx\_http\_voms\_module* are contained in the `t` folder. Setup and files to test the `ngx_http_voms_module` are contained in the [`t`](t) folder.
ngx_addon_name=ngx_http_voms_module
ngx_module_type=HTTP ngx_module_type=HTTP
ngx_addon_name=voms
ngx_module_name=ngx_http_voms_module ngx_module_name=ngx_http_voms_module
ngx_module_srcs="$ngx_addon_dir/src/ngx_http_voms_module.cpp" ngx_module_srcs="$ngx_addon_dir/src/ngx_http_voms_module.cpp"
ngx_module_libs="-lvomsapi -lstdc++" ngx_module_libs="-lvomsapi -lstdc++"
......
echo "objs/addon/src/ngx_http_voms_module.o: CFLAGS += -Werror" >> $NGX_MAKEFILE
DOCKER_IMAGE=cnafsd/nginx-httpg-voms
DOCKER_VERBOSE=y
DOCKER_GIT_TAG_ENABLED=y
# Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
# SPDX-License-Identifier: EUPL-1.2
ARG EL_VERSION=9
FROM almalinux:${EL_VERSION}
# https://docs.docker.com/reference/dockerfile/#understand-how-arg-and-from-interact
ARG EL_VERSION
# Allow customization of nginx user ID and name
ARG USERNAME=nginx
ARG USER_UID=1000
ARG USER_GID=${USER_UID}
# install dependencies
COPY library-scripts/*.sh /tmp/library-scripts/
RUN dnf update -y && \
sh /tmp/library-scripts/provide-deps.sh && \
sh /tmp/library-scripts/provide-user.sh ${USERNAME} ${USER_UID} ${USER_GID} && \
mkdir /pkgs && \
dnf clean all && rm -rf /var/cache/dnf
COPY artifacts/*.rpm /pkgs/
# install nginx httpg + voms and njs dynamic modules (latest version)
COPY nginx.repo /etc/yum.repos.d/nginx.repo
RUN rpm -ivh /pkgs/nginx-*.el${EL_VERSION}.httpg.x86_64.rpm && \
rpm -ivh /pkgs/nginx-module-http-voms-*.el${EL_VERSION}.x86_64.rpm && \
dnf install -y nginx-module-njs \
# forward request and error logs to docker log collector
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log
# install nginx + voms and njs dynamic modules
# RUN dnf -y install nginx nginx-module-njs && \
# rpm -ivh /pkgs/nginx-module-http-voms-1.24.0-1.el${EL_VERSION}.x86_64.rpm
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["nginx", "-g", "daemon off;"]
#!/usr/bin/env bash
# Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
# SPDX-License-Identifier: EUPL-1.2
set -ex
dnf -y install epel-release wget
# https://openresty.org/en/linux-packages.html#centos
wget https://openresty.org/package/centos/openresty2.repo
mv openresty2.repo /etc/yum.repos.d/openresty.repo
dnf config-manager --set-enabled crb
dnf -y install \
hostname \
which \
tar \
sudo \
file \
readline \
gettext \
less \
openssl \
zlib-devel \
pcre2-devel \
boost-devel \
voms-devel \
patch \
gcc-c++ \
rpmdevtools \
rpmlint \
perl-ExtUtils-Embed \
perl-Test-Nginx \
perl-Digest-SHA \
cpan \
voms-clients-cpp \
procps-ng
#!/usr/bin/env bash
# Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
# SPDX-License-Identifier: EUPL-1.2
USERNAME=${1}
USER_UID=${2}
USER_GID=${3}
set -ex
groupadd --gid $USER_GID $USERNAME
useradd --uid $USER_UID --gid $USER_GID -m $USERNAME
echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME
chmod 0440 /etc/sudoers.d/$USERNAME
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
diff --git a/src/http/ngx_http_parse.c b/src/http/ngx_http_parse.c
index d9a1dbed..7438816e 100644
--- a/src/http/ngx_http_parse.c
+++ b/src/http/ngx_http_parse.c
@@ -149,7 +149,13 @@ ngx_http_parse_request_line(ngx_http_request_t *r, ngx_buf_t *b)
break;
}
- if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
+ if (ch == '0') {
+ // httpg with no delegation
+ // eat the character and continue with the rest of the request
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "httpg request w/o delegation");
+ r->request_start++;
+ // httpg with a delegation request would fail for an unknown method
+ } else if ((ch < 'A' || ch > 'Z') && ch != '_' && ch != '-') {
return NGX_HTTP_PARSE_INVALID_METHOD;
}
#!/usr/bin/env bash
set -ex
# Add the httpg patch
# Set the nginx spec file with the httpg patch
PATCH_NAME="nginx-httpg_no_delegation.patch"
SPEC_FILE="${HOME}/rpmbuild/SPECS/nginx.spec"
if grep --extended-regexp --quiet "^Patch.*: $PATCH_NAME" "$SPEC_FILE"; then
>&2 echo "The patch $PATCH_NAME is already included in the spec file $SPEC_FILE"
exit 0
fi
# Copy the patch in the rpmbuild directory
cp ${CI_PROJECT_DIR}/${PATCH_NAME} ~/rpmbuild/SOURCES/
pushd ~/rpmbuild/SOURCES/
# Find the highest existing Patch number in the spec file
LAST_PATCH_NUM=$(grep -oP "^Patch\K[0-9]+" $SPEC_FILE | sort -n | tail -1)
if [ -z "$LAST_PATCH_NUM" ]; then
# There are no existing patches: find the highest Source number in the spec file
LAST_SOURCE_NUM=$(grep -oP "^Source\K[0-9]+" $SPEC_FILE | sort -n | tail -1)
# Add the patch to the spec file after the last Source
sed -i.backup "/^Source${LAST_SOURCE_NUM}/a Patch0: ${PATCH_NAME}" "${SPEC_FILE}"
else
# Add the new patch to the spec file after the last Patch
sed -i.backup "/^Patch${LAST_PATCH_NUM}/a Patch$((LAST_PATCH_NUM + 1)): ${PATCH_NAME}" "${SPEC_FILE}"
fi
# Add httpg to release
sed -i '/%define base_release/ s/ngx/httpg/' "${SPEC_FILE}"
diff "${SPEC_FILE}.backup" "${SPEC_FILE}" || true
popd
%define nginx_user nginx
%define nginx_group nginx
%define base_version 1.26.2
%define bdir %{_builddir}/%{name}-%{base_version}
Name: nginx-module-http-voms
Version: %{base_version}
Release: 1%{?dist}
Summary: nginx http voms dynamic modules
License: EUPL-1.2
URL: https://baltig.infn.it/cnafsd/ngx_http_voms_module
Source0: https://nginx.org/download/nginx-%{base_version}.tar.gz
Source1: ngx-http-voms-module
BuildRequires: gcc, make
BuildRequires: voms-devel
BuildRequires: boost-devel
BuildRequires: openssl-devel
BuildRequires: zlib-devel
BuildRequires: pcre2-devel
Requires: nginx
Requires: zlib
Requires: openssl
Requires: pcre2
Requires: voms
%description
nginx http voms dynamic modules.
%prep
%setup -qcTn %{name}-%{base_version}
tar --strip-components=1 -zxf %{SOURCE0}
%define CONFIG_PATH %(echo "--prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp")
%define CONFIG_ARGS %(echo "--user=nginx --group=nginx --with-compat --with-http_ssl_module")
%define MODULE_CONFIG_ARGS %(echo "--add-dynamic-module=%SOURCE1")
%build
cd %{bdir}
./configure %{CONFIG_PATH} %{CONFIG_ARGS} %{MODULE_CONFIG_ARGS}
make %{?_smp_mflags} modules
%install
cd %{bdir}
%{__rm} -rf $RPM_BUILD_ROOT
%{__mkdir} -p $RPM_BUILD_ROOT%{_libdir}/nginx/modules
for so in `find %{bdir}/objs/ -maxdepth 2 -type f -name "*.so"`; do
%{__install} -m755 $so \
$RPM_BUILD_ROOT%{_libdir}/nginx/modules/
done
%clean
%{__rm} -rf $RPM_BUILD_ROOT
%files
%{_libdir}/nginx/modules/*
%post
if [ $1 -eq 1 ]; then
cat <<BANNER
----------------------------------------------------------------------
The http voms dynamic modules for nginx have been installed.
To enable these modules, add the following to /etc/nginx/nginx.conf
and reload nginx:
load_module modules/ngx_http_voms_module.so;
Please refer to the modules documentation for further details:
https://baltig.infn.it/cnafsd/ngx_http_voms_module
----------------------------------------------------------------------
BANNER
fi
%changelog
* Fri Oct 18 2024 Francesco Giacomini
- nginx http voms module updated to 1.26.2-1.0.0
* Wed Apr 12 2023 Laura Cappelli
- nginx http voms module updated to 1.24.0-1.0.0
* Fri Nov 18 2022 Laura Cappelli
- nginx http voms module updated to 1.22.1-1.0.0
// Copyright 2018 Istituto Nazionale di Fisica Nucleare // Copyright 2018-2023 Istituto Nazionale di Fisica Nucleare
// // SPDX-License-Identifier: EUPL-1.2
// Licensed under the EUPL, Version 1.2 or - as soon they will be approved by
// the European Commission - subsequent versions of the EUPL (the "Licence").
// You may not use this work except in compliance with the Licence. You may
// obtain a copy of the Licence at:
//
// https://joinup.ec.europa.eu/software/page/eupl
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the Licence is distributed on an "AS IS" basis, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// Licence for the specific language governing permissions and limitations under
// the Licence.
extern "C" { extern "C" {
#include <ngx_config.h> #include <ngx_config.h>
...@@ -22,28 +10,35 @@ extern "C" { ...@@ -22,28 +10,35 @@ extern "C" {
#include <cassert> #include <cassert>
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <map>
#include <memory> #include <memory>
#include <numeric> #include <numeric>
#include <string> #include <string>
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/version.hpp>
using BioPtr = std::unique_ptr<BIO, decltype(&BIO_free)>; using BioPtr = std::unique_ptr<BIO, decltype(&BIO_free)>;
using X509Ptr = std::unique_ptr<X509, decltype(&X509_free)>; using X509Ptr = std::unique_ptr<X509, decltype(&X509_free)>;
using VomsAc = voms; using VomsAc = voms;
using MaybeVomsAc = boost::optional<VomsAc>; using MaybeVomsAc = boost::optional<VomsAc>;
enum class EeDn { SUBJECT, ISSUER };
static ngx_int_t add_variables(ngx_conf_t* cf); static ngx_int_t add_variables(ngx_conf_t* cf);
static ngx_int_t ngx_ssl_allow_proxy_certs(ngx_ssl_t* ssl);
static char* ngx_http_voms_merge_srv_conf(ngx_conf_t* cf, void*, void*);
static ngx_http_module_t ctx = { static ngx_http_module_t ctx = {
add_variables, // preconfiguration add_variables, // preconfiguration
NULL, // postconfiguration NULL, // postconfiguration
NULL, // create main configuration NULL, // create main configuration
NULL, // init main configuration NULL, // init main configuration
NULL, // create server configuration NULL, // create server configuration
NULL, // merge server configuration ngx_http_voms_merge_srv_conf, // merge server configuration
NULL, // create location configuration NULL, // create location configuration
NULL // merge location configuration NULL // merge location configuration
}; };
ngx_module_t ngx_http_voms_module = { ngx_module_t ngx_http_voms_module = {
...@@ -61,13 +56,21 @@ ngx_module_t ngx_http_voms_module = { ...@@ -61,13 +56,21 @@ ngx_module_t ngx_http_voms_module = {
NGX_MODULE_V1_PADDING // NGX_MODULE_V1_PADDING //
}; };
static std::unique_ptr<vomsdata> vomsdata_ptr;
static ngx_int_t generic_getter( // static ngx_int_t generic_getter( //
ngx_http_request_t* r, ngx_http_request_t* r,
ngx_http_variable_value_t* v, ngx_http_variable_value_t* v,
uintptr_t data); uintptr_t data);
static ngx_int_t get_ssl_client_ee_dn( //
ngx_http_request_t* r,
ngx_http_variable_value_t* v,
uintptr_t data);
static ngx_int_t get_ssl_client_ee_cert( //
ngx_http_request_t* r,
ngx_http_variable_value_t* v,
uintptr_t data);
using getter_t = std::string(VomsAc const& voms); using getter_t = std::string(VomsAc const& voms);
static getter_t get_voms_user; static getter_t get_voms_user;
static getter_t get_voms_user_ca; static getter_t get_voms_user_ca;
...@@ -170,6 +173,30 @@ static ngx_http_variable_t variables[] = { ...@@ -170,6 +173,30 @@ static ngx_http_variable_t variables[] = {
NGX_HTTP_VAR_NOCACHEABLE, NGX_HTTP_VAR_NOCACHEABLE,
0 // 0 //
}, },
{
ngx_string("ssl_client_ee_s_dn"),
NULL,
get_ssl_client_ee_dn,
static_cast<uintptr_t>(EeDn::SUBJECT),
NGX_HTTP_VAR_NOCACHEABLE,
0 //
},
{
ngx_string("ssl_client_ee_i_dn"),
NULL,
get_ssl_client_ee_dn,
static_cast<uintptr_t>(EeDn::ISSUER),
NGX_HTTP_VAR_NOCACHEABLE,
0 //
},
{
ngx_string("ssl_client_ee_cert"),
NULL,
get_ssl_client_ee_cert,
0,
NGX_HTTP_VAR_NOCACHEABLE,
0 //
},
ngx_http_null_variable // ngx_http_null_variable //
}; };
...@@ -188,12 +215,48 @@ static ngx_int_t add_variables(ngx_conf_t* cf) ...@@ -188,12 +215,48 @@ static ngx_int_t add_variables(ngx_conf_t* cf)
return NGX_OK; return NGX_OK;
} }
static ngx_int_t ngx_ssl_allow_proxy_certs(ngx_ssl_t* ssl)
{
X509_STORE* store = SSL_CTX_get_cert_store(ssl->ctx);
if (store == NULL) {
ngx_ssl_error(NGX_LOG_EMERG,
ssl->log,
0,
const_cast<char*>("SSL_CTX_get_cert_store() failed"));
return NGX_ERROR;
}
X509_STORE_set_flags(store, X509_V_FLAG_ALLOW_PROXY_CERTS);
return NGX_OK;
}
static char* ngx_http_voms_merge_srv_conf(ngx_conf_t* cf, void*, void*)
{
auto conf = static_cast<ngx_http_ssl_srv_conf_t*>(
ngx_http_conf_get_module_srv_conf(cf, ngx_http_ssl_module));
if (conf->ssl.ctx != nullptr) {
if (ngx_ssl_allow_proxy_certs(&conf->ssl) != NGX_OK) {
return static_cast<char*>(NGX_CONF_ERROR);
}
}
return NGX_CONF_OK;
}
// return the first AC, if present // return the first AC, if present
static MaybeVomsAc retrieve_voms_ac_from_proxy(ngx_http_request_t* r) static MaybeVomsAc retrieve_voms_ac_from_proxy(ngx_http_request_t* r)
{ {
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__); ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__);
if (!r->http_connection->ssl) { if (!r->main->http_connection->ssl) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "SSL not enabled");
return boost::none;
}
if (!r->connection->ssl) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "plain HTTP request");
return boost::none; return boost::none;
} }
...@@ -203,7 +266,7 @@ static MaybeVomsAc retrieve_voms_ac_from_proxy(ngx_http_request_t* r) ...@@ -203,7 +266,7 @@ static MaybeVomsAc retrieve_voms_ac_from_proxy(ngx_http_request_t* r)
ngx_log_error(NGX_LOG_ERR, ngx_log_error(NGX_LOG_ERR,
r->connection->log, r->connection->log,
0, 0,
"SSL_get_peer_certificate() failed"); "no SSL peer certificate available");
return boost::none; return boost::none;
} }
...@@ -214,57 +277,62 @@ static MaybeVomsAc retrieve_voms_ac_from_proxy(ngx_http_request_t* r) ...@@ -214,57 +277,62 @@ static MaybeVomsAc retrieve_voms_ac_from_proxy(ngx_http_request_t* r)
return boost::none; return boost::none;
} }
if (!vomsdata_ptr) { vomsdata vd;
vomsdata_ptr.reset(new vomsdata);
} auto ok = vd.Retrieve(client_cert.get(), client_chain, RECURSE_CHAIN);
auto ok =
vomsdata_ptr->Retrieve(client_cert.get(), client_chain, RECURSE_CHAIN);
if (!ok) { if (!ok) {
// vd.error is not interpreted correctly by the logger, which probably uses auto msg = vd.ErrorMessage();
// errno ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "%s", msg.c_str());
ngx_log_error(NGX_LOG_ERR,
r->connection->log,
vomsdata_ptr->error,
"%s",
vomsdata_ptr->ErrorMessage().c_str());
return boost::none; return boost::none;
} }
if (vomsdata_ptr->data.empty()) { if (vd.data.empty()) {
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "no ACs in proxy"); ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "no ACs in proxy");
return boost::none; return boost::none;
} }
return vomsdata_ptr->data.front(); return vd.data.front();
} }
// note that an entry in the cache is always created if within a connection a
// request is made to one of the voms_* variables. If the presented
// credential is a normal certificate or a proxy without attribute certificates,
// the unique_ptr points to a MaybeVomsAc that is empty (in the sense of an
// std::optional)
static std::map<ngx_connection_t*, std::unique_ptr<MaybeVomsAc>> ac_cache;
static void clean_voms_ac(void* data) static void clean_voms_ac(void* data)
{ {
auto r = static_cast<ngx_http_request_t*>(data); auto c = static_cast<ngx_connection_t*>(data);
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__); ngx_log_error(NGX_LOG_DEBUG, c->log, 0, "%s", __func__);
auto n = ac_cache.erase(c);
auto p = static_cast<MaybeVomsAc*>( // we erase from the cache exactly once per connection
ngx_http_get_module_ctx(r, ngx_http_voms_module)); assert(n == 1);
delete p;
} }
static void cache_voms_ac(ngx_http_request_t* r, MaybeVomsAc* ac) static void cache_voms_ac(ngx_http_request_t* r,
std::unique_ptr<MaybeVomsAc> acp)
{ {
ngx_http_set_ctx(r, ac, ngx_http_voms_module); ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__);
auto cln = ngx_http_cleanup_add(r, 0); auto c = r->connection;
auto cln = ngx_pool_cleanup_add(c->pool, 0);
if (cln) { if (cln) {
auto r = ac_cache.insert(std::make_pair(c, std::move(acp)));
// we insert into the cache exactly once per connection
assert(r.second);
cln->handler = clean_voms_ac; cln->handler = clean_voms_ac;
cln->data = r; cln->data = c;
} else { } else {
ngx_log_error( ngx_log_error(
NGX_LOG_ERR, r->connection->log, 0, "ngx_http_cleanup_add() failed"); NGX_LOG_ERR, r->connection->log, 0, "ngx_pool_cleanup_add() failed");
} }
} }
static MaybeVomsAc* get_voms_ac_from_cache(ngx_http_request_t* r) static MaybeVomsAc* get_voms_ac_from_cache(ngx_http_request_t* r)
{ {
return static_cast<MaybeVomsAc*>( ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__);
ngx_http_get_module_ctx(r, ngx_http_voms_module)); auto it = ac_cache.find(r->connection);
return it != ac_cache.end() ? it->second.get() : nullptr;
} }
static MaybeVomsAc const& get_voms_ac(ngx_http_request_t* r) static MaybeVomsAc const& get_voms_ac(ngx_http_request_t* r)
...@@ -274,8 +342,9 @@ static MaybeVomsAc const& get_voms_ac(ngx_http_request_t* r) ...@@ -274,8 +342,9 @@ static MaybeVomsAc const& get_voms_ac(ngx_http_request_t* r)
MaybeVomsAc* acp = get_voms_ac_from_cache(r); MaybeVomsAc* acp = get_voms_ac_from_cache(r);
if (!acp) { if (!acp) {
acp = new MaybeVomsAc(retrieve_voms_ac_from_proxy(r)); std::unique_ptr<MaybeVomsAc> p{new MaybeVomsAc(retrieve_voms_ac_from_proxy(r))};
cache_voms_ac(r, acp); acp = p.get();
cache_voms_ac(r, std::move(p));
} }
return *acp; return *acp;
...@@ -303,7 +372,6 @@ static ngx_int_t generic_getter(ngx_http_request_t* r, ...@@ -303,7 +372,6 @@ static ngx_int_t generic_getter(ngx_http_request_t* r,
auto buffer = static_cast<u_char*>(ngx_pnalloc(r->pool, value.size())); auto buffer = static_cast<u_char*>(ngx_pnalloc(r->pool, value.size()));
if (!buffer) { if (!buffer) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "ngx_pnalloc() failed");
return NGX_OK; return NGX_OK;
} }
ngx_memcpy(buffer, value.c_str(), value.size()); ngx_memcpy(buffer, value.c_str(), value.size());
...@@ -361,19 +429,10 @@ std::string get_voms_not_after(VomsAc const& ac) ...@@ -361,19 +429,10 @@ std::string get_voms_not_after(VomsAc const& ac)
return ac.date2; return ac.date2;
} }
// struct attribute { static std::string escape_uri(std::string const& src)
// std::string name; /*!< attribute's group */
// std::string qualifier; /*!< attribute's qualifier */
// std::string value; /*!< attribute's value */
// };
// struct attributelist {
// std::string grantor; /*!< Who granted these attributes. */
// std::vector<attribute> attributes; /*!< The attributes themselves. */
// };
std::string escape_uri(std::string const& src)
{ {
std::string result = src;
// the following just counts the number of characters that need escaping // the following just counts the number of characters that need escaping
auto const n_escape = auto const n_escape =
ngx_escape_uri(nullptr, // <-- ngx_escape_uri(nullptr, // <--
...@@ -381,18 +440,14 @@ std::string escape_uri(std::string const& src) ...@@ -381,18 +440,14 @@ std::string escape_uri(std::string const& src)
src.size(), src.size(),
NGX_ESCAPE_URI_COMPONENT); NGX_ESCAPE_URI_COMPONENT);
if (n_escape == 0) { if (n_escape > 0) {
return src; result.resize(src.size() + 2 * n_escape);
ngx_escape_uri(reinterpret_cast<u_char*>(const_cast<char*>(result.data())),
reinterpret_cast<u_char*>(const_cast<char*>(src.data())),
src.size(),
NGX_ESCAPE_URI_COMPONENT);
} }
std::string result;
result.resize(src.size() + 2 * n_escape);
auto last = reinterpret_cast<char*>(ngx_escape_uri(
reinterpret_cast<u_char*>(const_cast<char*>(result.data())),
reinterpret_cast<u_char*>(const_cast<char*>(src.data())),
src.size(),
NGX_ESCAPE_URI_COMPONENT));
assert(last == &result.back());
return result; return result;
} }
...@@ -427,3 +482,224 @@ std::string get_voms_serial(VomsAc const& ac) ...@@ -427,3 +482,224 @@ std::string get_voms_serial(VomsAc const& ac)
{ {
return ac.serial; return ac.serial;
} }
static std::string to_rfc2253(X509_NAME* name)
{
std::string result;
BioPtr bio(BIO_new(BIO_s_mem()), &BIO_free);
if (!bio) {
return result;
}
if (X509_NAME_print_ex(bio.get(), name, 0, XN_FLAG_RFC2253) < 0) {
return result;
}
auto len = BIO_pending(bio.get());
result.resize(len);
BIO_read(bio.get(), &result[0], result.size());
return result;
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static uint32_t X509_get_extension_flags(X509* x)
{
return x->ex_flags;
}
#endif
static bool is_ca(X509* cert)
{
return X509_check_ca(cert) != 0;
}
static bool is_proxy(X509* cert)
{
return X509_get_extension_flags(cert) & EXFLAG_PROXY;
}
static X509* get_ee_cert(ngx_http_request_t* r)
{
auto chain = SSL_get_peer_cert_chain(r->connection->ssl->connection);
if (!chain) {
ngx_log_error(
NGX_LOG_ERR, r->connection->log, 0, "SSL_get_peer_cert_chain() failed");
return nullptr;
}
X509* ee_cert = nullptr;
if (sk_X509_num(chain) == 0) {
ee_cert = SSL_get_peer_certificate(r->connection->ssl->connection);
} else {
// find first non-proxy and non-ca cert
for (int i = 0; i != sk_X509_num(chain); ++i) {
auto cert = sk_X509_value(chain, i);
if (cert && is_ca(cert)) {
break;
}
if (cert && !is_proxy(cert)) {
ee_cert = cert;
break;
}
}
if (!ee_cert) {
ee_cert = SSL_get_peer_certificate(r->connection->ssl->connection);
}
}
return ee_cert;
}
static ngx_int_t get_ssl_client_ee_dn(ngx_http_request_t* r,
ngx_http_variable_value_t* v,
uintptr_t data)
{
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__);
v->not_found = 1;
v->valid = 0;
auto ee_cert = get_ee_cert(r);
if (!ee_cert) {
ngx_log_error(NGX_LOG_DEBUG,
r->connection->log,
0,
"cannot identify end-entity certificate");
return NGX_OK;
}
X509_NAME* dn;
switch (static_cast<EeDn>(data)) {
case EeDn::SUBJECT:
dn = X509_get_subject_name(ee_cert);
break;
case EeDn::ISSUER:
dn = X509_get_issuer_name(ee_cert);
break;
default:
dn = nullptr;
}
if (!dn) {
ngx_log_error(
NGX_LOG_DEBUG, r->connection->log, 0, "cannot get DN from certificate");
return NGX_OK;
}
std::string value = to_rfc2253(dn);
auto buffer = static_cast<u_char*>(ngx_pnalloc(r->pool, value.size()));
if (!buffer) {
return NGX_OK;
}
ngx_memcpy(buffer, value.c_str(), value.size());
v->data = buffer;
v->len = value.size();
v->valid = 1;
v->not_found = 0;
v->no_cacheable = 0;
return NGX_OK;
}
static ngx_int_t get_ssl_client_ee_cert_raw(ngx_http_request_t* r,
ngx_str_t* result)
{
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__);
*result = {0, nullptr};
auto ee_cert = get_ee_cert(r);
if (!ee_cert) {
ngx_log_error(NGX_LOG_DEBUG,
r->connection->log,
0,
"cannot identify end-entity certificate");
return NGX_OK;
}
BioPtr bio(BIO_new(BIO_s_mem()), &BIO_free);
if (!bio) {
ngx_log_error(
NGX_LOG_ERR, r->connection->log, 0, "cannot create OpenSSL BIO");
return NGX_ERROR;
}
if (PEM_write_bio_X509(bio.get(), ee_cert) == 0) {
ngx_log_error(
NGX_LOG_ERR, r->connection->log, 0, "cannot write EEC to OpenSSL BIO");
return NGX_ERROR;
}
result->len = BIO_pending(bio.get());
result->data = static_cast<u_char*>(ngx_pnalloc(r->pool, result->len));
if (result->data == nullptr) {
return NGX_ERROR;
}
BIO_read(bio.get(), result->data, result->len);
return NGX_OK;
}
#if BOOST_VERSION < 105600
namespace boost {
template <typename IteratorT, typename IntegerT>
inline iterator_range<IteratorT> make_iterator_range_n(IteratorT first,
IntegerT n)
{
return iterator_range<IteratorT>(first, boost::next(first, n));
}
} // namespace boost
#endif
static ngx_int_t get_ssl_client_ee_cert(ngx_http_request_t* r,
ngx_http_variable_value_t* v,
uintptr_t data)
{
ngx_log_error(NGX_LOG_DEBUG, r->connection->log, 0, "%s", __func__);
v->not_found = 1;
v->valid = 0;
ngx_str_t cert{0, nullptr};
if (get_ssl_client_ee_cert_raw(r, &cert) != NGX_OK) {
return NGX_ERROR;
}
if (cert.len == 0) {
v->len = 0;
return NGX_OK;
}
// the first line is not prepended with a tab
auto const n_tabs = std::count(cert.data, cert.data + cert.len, '\n') - 1;
// cert is null-terminated
auto const len = cert.len - 1 + n_tabs;
auto const buffer = static_cast<u_char*>(ngx_pnalloc(r->pool, len));
if (!buffer) {
return NGX_OK;
}
// the last newline is not to be followed by a tab
boost::algorithm::replace_all_copy(
buffer, boost::make_iterator_range_n(cert.data, len - 1), "\n", "\n\t");
v->data = buffer;
v->len = len;
v->valid = 1;
v->not_found = 0;
v->no_cacheable = 0;
return NGX_OK;
}