mirror of
https://gitlab.kit.edu/kit/scc/sys/mail/exim-encrypt-dlfunc.git
synced 2025-12-06 10:53:55 +01:00
Compare commits
45 Commits
0.2.0
...
1c84639ac7
| Author | SHA1 | Date | |
|---|---|---|---|
| 1c84639ac7 | |||
| 64dc7481cb | |||
| a8cbac240a | |||
| db9d8d6ee4 | |||
| d224ae8124 | |||
| 05ef7c4467 | |||
| ea589221b4 | |||
| 0a5e880f32 | |||
| b02f9e40ab | |||
| 781c716d8e | |||
| 1062248787 | |||
| 36fcdfaf42 | |||
| b32d6fc861 | |||
| 15de3a6204 | |||
| 643f1a6719 | |||
| 5bf5bdecb1 | |||
| b19a1fc673 | |||
| c05f162a28 | |||
| c7eedb7f4c | |||
| 32e060d88d | |||
| e26daf675b | |||
| e1968e8f8c | |||
| a6c6169122 | |||
| b6a350ef3a | |||
| 0d8fb3dd77 | |||
| 57b829737e | |||
| b6726dfcd2 | |||
| 8845aaa653 | |||
| 649932c73c | |||
| 99ff9e359c | |||
| b8a5a2c759 | |||
| 1f8aa5fc4c | |||
| 5a1bd58452 | |||
| bb67fe5ba2 | |||
| 534db3ad6e | |||
| ad3437f5df | |||
| 41e7c43ab8 | |||
| 0b01283cd9 | |||
| a82f6d388b | |||
| aaad6b0e4f | |||
| 71b3950c03 | |||
| aa8f4c5e02 | |||
| 565aa43fb2 | |||
| 8c0340df0e | |||
| b944a41f7c |
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,2 +1,3 @@
|
|||||||
/build
|
/build
|
||||||
|
debian/*.ex
|
||||||
|
.gdb_history
|
||||||
108
.gitlab-ci.yml
108
.gitlab-ci.yml
@ -1,39 +1,115 @@
|
|||||||
stages:
|
stages:
|
||||||
- build
|
- build
|
||||||
- test
|
- debian-package
|
||||||
|
|
||||||
|
.image-buster:
|
||||||
|
image: '${CONTAINER_REGISTRY_NAME}/exim-encrypt-dlfunc-build-buster'
|
||||||
|
|
||||||
|
.image-bullseye:
|
||||||
|
image: '${CONTAINER_REGISTRY_NAME}/exim-encrypt-dlfunc-build-bullseye'
|
||||||
|
|
||||||
|
.image-focal:
|
||||||
|
image: '${CONTAINER_REGISTRY_NAME}/exim-encrypt-dlfunc-build-focal'
|
||||||
|
|
||||||
|
.code-coverage:
|
||||||
|
stage: build
|
||||||
|
script:
|
||||||
|
- "export PATH=/usr/local/sbin:/usr/local/bin:${PATH}"
|
||||||
|
- meson build -Db_coverage=true
|
||||||
|
- cd build
|
||||||
|
- ninja
|
||||||
|
- ninja test
|
||||||
|
- ninja coverage-xml
|
||||||
|
- grep -Eo 'line-rate="[^"]+"' meson-logs/coverage.xml | head -n 1 |
|
||||||
|
grep -Eo '[0-9.]+' | awk '{ printf "coverage\x3a %.2f%% of statements\n", $1 * 100 }'
|
||||||
|
- ninja coverage-html
|
||||||
|
- mv meson-logs/coveragereport ../coverage
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- coverage/
|
||||||
|
reports:
|
||||||
|
cobertura: build/meson-logs/coverage.xml
|
||||||
|
|
||||||
.build:
|
.build:
|
||||||
stage: build
|
stage: build
|
||||||
before_script:
|
|
||||||
- apt-get update
|
|
||||||
- DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install -y build-essential exim4-dev libsodium-dev pkg-config python3-pip exim4-daemon-heavy openssl
|
|
||||||
- pip3 install meson ninja
|
|
||||||
script:
|
script:
|
||||||
|
- which meson
|
||||||
- meson build
|
- meson build
|
||||||
- cd build
|
- cd build
|
||||||
- ninja
|
- ninja
|
||||||
- ninja test
|
- ninja test
|
||||||
artifacts:
|
artifacts:
|
||||||
paths:
|
paths:
|
||||||
- build/src/generate_encryption_keys
|
|
||||||
- build/src/libexim-encrypt-dlfunc.so
|
- build/src/libexim-encrypt-dlfunc.so
|
||||||
|
- build/src/libexim-encrypt-dlfunc-genkeys
|
||||||
|
- build/src/libexim-encrypt-dlfunc-decrypt-secretbox
|
||||||
|
- build/src/libexim-encrypt-dlfunc-decrypt-sealedbox
|
||||||
|
|
||||||
|
.debian-package:
|
||||||
|
stage: debian-package
|
||||||
|
script:
|
||||||
|
- mkdir ~/.gnupg; chown root:root ~/.gnupg; chmod 700 ~/.gnupg
|
||||||
|
- eval $(gpg-agent --batch --sh --disable-scdaemon)
|
||||||
|
- echo "pinentry-mode loopback" > ~/.gnupg/gpg.conf
|
||||||
|
- echo "allow-loopback-pinentry" > ~/.gnupg/gpg-agent.conf
|
||||||
|
- gpg-connect-agent /bye
|
||||||
|
- echo $DEBIAN_SIGNING_KEY_ASC | base64 -d | gpg --batch --import --always-trust --yes
|
||||||
|
- echo "1DC7C2770DC111723D505DD61614D5CDEE1555A7:6:" | gpg --import-ownertrust
|
||||||
|
- dpkg-buildpackage --sign-key=1DC7C2770DC111723D505DD61614D5CDEE1555A7
|
||||||
|
- mv -t . ../*.deb ../*.dsc ../*.tar.gz ../*.changes ../*.buildinfo
|
||||||
|
artifacts:
|
||||||
|
paths:
|
||||||
|
- ./*.deb
|
||||||
|
- ./*.dsc
|
||||||
|
- ./*.tar.gz
|
||||||
|
- ./*.changes
|
||||||
|
- ./*.buildinfo
|
||||||
|
|
||||||
|
code-coverage:
|
||||||
|
extends:
|
||||||
|
- .image-bullseye
|
||||||
|
- .code-coverage
|
||||||
|
needs: [ ]
|
||||||
|
|
||||||
|
build:bullseye:
|
||||||
|
extends:
|
||||||
|
- .image-bullseye
|
||||||
|
- .build
|
||||||
|
- .code-coverage
|
||||||
|
needs: [ ]
|
||||||
|
|
||||||
|
build:focal:
|
||||||
|
extends:
|
||||||
|
- .image-focal
|
||||||
|
- .build
|
||||||
|
needs: [ ]
|
||||||
|
|
||||||
build:buster:
|
build:buster:
|
||||||
extends:
|
extends:
|
||||||
- .build
|
- .build
|
||||||
image: debian:buster
|
- .image-buster
|
||||||
|
needs: [ ]
|
||||||
|
|
||||||
build:bullseye:
|
debian-package:bullseye:
|
||||||
image: debian:bullseye
|
|
||||||
extends:
|
extends:
|
||||||
- .build
|
- .image-bullseye
|
||||||
|
- .debian-package
|
||||||
|
dependencies:
|
||||||
|
- build:bullseye
|
||||||
|
needs: [ "build:bullseye" ]
|
||||||
|
|
||||||
build:bionic:
|
debian-package:focal:
|
||||||
image: ubuntu:bionic
|
|
||||||
extends:
|
extends:
|
||||||
- .build
|
- .image-focal
|
||||||
|
- .debian-package
|
||||||
|
dependencies:
|
||||||
|
- build:focal
|
||||||
|
needs: [ "build:focal" ]
|
||||||
|
|
||||||
build:focal:
|
debian-package:buster:
|
||||||
image: ubuntu:focal
|
|
||||||
extends:
|
extends:
|
||||||
- .build
|
- .image-buster
|
||||||
|
- .debian-package
|
||||||
|
dependencies:
|
||||||
|
- build:buster
|
||||||
|
needs: [ "build:buster" ]
|
||||||
|
|||||||
126
README.md
126
README.md
@ -3,8 +3,14 @@
|
|||||||
This library injects functions for string encryption and decryption into [exim4](https://www.exim.org/). It is basically
|
This library injects functions for string encryption and decryption into [exim4](https://www.exim.org/). It is basically
|
||||||
glue code that exports certain parts of the [libsodium library](https://github.com/jedisct1/libsodium) to exim at runtime.
|
glue code that exports certain parts of the [libsodium library](https://github.com/jedisct1/libsodium) to exim at runtime.
|
||||||
|
|
||||||
|
[[_TOC_]]
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
Beta quality debian packages are currently available as pipeline artifacts. Proper releases are work in progress (#6).
|
||||||
|
|
||||||
|
## Compiling
|
||||||
|
|
||||||
These instructions are currently only tested on [Debian](https://www.debian.org) and
|
These instructions are currently only tested on [Debian](https://www.debian.org) and
|
||||||
[Ubuntu](https://ubuntu.com).
|
[Ubuntu](https://ubuntu.com).
|
||||||
|
|
||||||
@ -14,16 +20,16 @@ These instructions are currently only tested on [Debian](https://www.debian.org)
|
|||||||
apt-get install -y build-essential exim4-dev libsodium-dev meson pkg-config openssl exim4-daemon-heavy
|
apt-get install -y build-essential exim4-dev libsodium-dev meson pkg-config openssl exim4-daemon-heavy
|
||||||
```
|
```
|
||||||
|
|
||||||
`exim-encrypt-dlfunc` uses [meson](https://mesonbuild.com/) (and [ninja](https://ninja-build.org/)) as build system. To
|
This project uses [meson](https://mesonbuild.com/) (and [ninja](https://ninja-build.org/)) as build system. To ensure
|
||||||
ensure all features are present (which are lacking in the version packages in older distributions)
|
all features are present (which are lacking in the packaged versions of older distributions)
|
||||||
it is advised to install from [pypi](https://pypi.org/) via [pip](https://pypi.org/project/pip/):
|
you are strongly advised to install from [pypi](https://pypi.org/) via [pip](https://pypi.org/project/pip/):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
apt-get install -y python3-pip
|
apt-get install -y python3-pip
|
||||||
pip3 install meson ninja
|
pip3 install -U meson ninja
|
||||||
```
|
```
|
||||||
|
|
||||||
You may alternatively use your distribution packages:
|
You may alternatively try to use your distribution packages:
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
apt-get install -y meson
|
apt-get install -y meson
|
||||||
@ -44,7 +50,10 @@ meson compile -C build
|
|||||||
meson test -C build
|
meson test -C build
|
||||||
```
|
```
|
||||||
|
|
||||||
5. Copy to final destination (feel free to pick another place than `/usr/local/lib/`):
|
The `ci_container` directory contains a [script](ci_container/build.sh) (and a [short README](ci_container/README.md))
|
||||||
|
which creates the container images used in continuous integration for this project.
|
||||||
|
|
||||||
|
5. Install library and tools (defaults to `/usr/local/bin` and `/usr/local/lib/x86_64-linux-gnu`):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
meson install -C build
|
meson install -C build
|
||||||
@ -54,31 +63,47 @@ meson install -C build
|
|||||||
|
|
||||||
Not every build of exim is able to load libraries at runtime. Please refer to the
|
Not every build of exim is able to load libraries at runtime. Please refer to the
|
||||||
[documentation](https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html)
|
[documentation](https://www.exim.org/exim-html-current/doc/html/spec_html/ch-string_expansions.html)
|
||||||
of the `${dlfunc{…}}` function for details. exim from the debian package `exim4-daemon-heavy` meets all the
|
of the `${dlfunc{…}}` function for details. The Debian
|
||||||
requirements.
|
package [`exim4-daemon-heavy`](https://packages.debian.org/exim4-daemon-heavy)
|
||||||
|
meets these requirements.
|
||||||
|
|
||||||
|
Try
|
||||||
|
|
||||||
|
```shell
|
||||||
|
exim4 --version | egrep -i --color 'Expand_dlfunc|Content_Scanning'
|
||||||
|
```
|
||||||
|
|
||||||
|
for a preliminary test.
|
||||||
|
|
||||||
|
7. Configure exim to use the new functionality
|
||||||
|
|
||||||
|
This highly depends on your use case. See [the example below](#xoip-example) and
|
||||||
|
[`src/test_libexim-encrypt-dlfunc.sh`](src/test_libexim-encrypt-dlfunc.sh).
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
There are currently two pairs of functions:
|
There are currently two sets of complementary function pairs:
|
||||||
|
|
||||||
Symmetric encryption that derives its key from an ASCII string:
|
### Symmetric encryption that derives the key from an ASCII string (aka a “password”)
|
||||||
|
|
||||||
* `sodium_crypto_secretbox_encrypt_password(password, cleartext) → ciphertext`
|
* `sodium_crypto_secretbox_encrypt_password(password, cleartext) → ciphertext`
|
||||||
* `sodium_crypto_secretbox_decrypt_password(password, ciphertext) → cleartext`
|
* `sodium_crypto_secretbox_decrypt_password(password, ciphertext) → cleartext`
|
||||||
|
|
||||||
The generated key is only as strong as the provided password.
|
The generated key is only as strong as the provided password. Use `openssl rand --base64 48 | tr -d =`
|
||||||
|
to get a passable password.
|
||||||
|
|
||||||
Public key encryption that uses a key pair that needs to be created beforehand:
|
### Public key encryption that uses a key pair which needs to be created beforehand
|
||||||
|
|
||||||
* `sodium_crypto_box_seal(public key, cleartext) → ciphertext`
|
* `sodium_crypto_box_seal(public key, cleartext) → ciphertext`
|
||||||
* `sodium_crypto_box_seal_open(private key, public key, ciphertext) → cleartext`
|
* `sodium_crypto_box_seal_open(private key, public key, ciphertext) → cleartext`
|
||||||
|
|
||||||
The second pair needs a proper key pair in the correct format. This is what the
|
The second pair needs a matching key pair in the correct format. This is what the
|
||||||
`generate_encryption_keys` utility is for. Simply run it once to generate a pair. Be aware that every invocation will
|
`libexim-encrypt-dlfunc-genkeys` utility is for. Simply run it once to generate a pair in the current directory. Be
|
||||||
overwrite the previous key pair without confirmation! Please save both parts in a safe place before proceeding.
|
aware that every invocation will overwrite the previous key pair file without confirmation! Make sure to store your
|
||||||
|
production keys in a safe place!
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ ./generate_encryption_keys
|
$ libexim-encrypt-dlfunc-genkeys
|
||||||
=== Creating cryptobox key pair ===
|
=== Creating cryptobox key pair ===
|
||||||
Wrote »cryptobox_recipient_pk_exim.conf«
|
Wrote »cryptobox_recipient_pk_exim.conf«
|
||||||
Wrote »cryptobox_recipient_pk.raw«
|
Wrote »cryptobox_recipient_pk.raw«
|
||||||
@ -87,11 +112,13 @@ $ ./generate_encryption_keys
|
|||||||
```
|
```
|
||||||
|
|
||||||
The `*_exim.conf` files contain the keys in a format that can simply be used in
|
The `*_exim.conf` files contain the keys in a format that can simply be used in
|
||||||
`exim.conf` (the first line contains the key as a C code comment and can usually be discarded):
|
`exim.conf` (the first line contains the key as a C code comment and can usually be ignored or discarded):
|
||||||
|
|
||||||
```shell
|
```shell
|
||||||
$ cat cryptobox_recipient_pk_exim.conf
|
$ cat cryptobox_recipient_pk_exim.conf
|
||||||
# const unsigned char cryptobox_recipient_pk[32] = { 0xda, 0x46, 0xc8, 0x75, 0x2b, 0x31, 0xd9, 0x0c, 0x83, 0x54, 0x2d, 0x18, 0xda, 0xdc, 0xe5, 0x2d, 0x0e, 0x10, 0xe8, 0x0c, 0x39, 0xde, 0xaf, 0x30, 0x7e, 0xab, 0xca, 0x4d, 0xed, 0x26, 0x4d, 0x6e }; const unsigned int cryptobox_recipient_pk_length = 32;
|
# const unsigned char cryptobox_recipient_pk[32] = { 0xda, 0x46, 0xc8, 0x75, 0x2b, 0x31, 0xd9, 0x0c, 0x83, 0x54, 0x2d,
|
||||||
|
# 0x18, 0xda, 0xdc, 0xe5, 0x2d, 0x0e, 0x10, 0xe8, 0x0c, 0x39, 0xde, 0xaf, 0x30, 0x7e, 0xab, 0xca, 0x4d, 0xed, 0x26,
|
||||||
|
# 0x4d, 0x6e }; const unsigned int cryptobox_recipient_pk_length = 32;
|
||||||
CRYPTOBOX_RECIPIENT_PK=2kbIdSsx2QyDVC0Y2tzlLQ4Q6Aw53q8wfqvKTe0mTW4=
|
CRYPTOBOX_RECIPIENT_PK=2kbIdSsx2QyDVC0Y2tzlLQ4Q6Aw53q8wfqvKTe0mTW4=
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -100,18 +127,19 @@ generated as convenience when writing your own tools.
|
|||||||
|
|
||||||
### Example: remove `X-Originating-IP:` header
|
### Example: remove `X-Originating-IP:` header
|
||||||
|
|
||||||
This example's use case was the initial reason to develop this library: remove the X-Originating-IP header to preserve
|
<a name="xoip-example"></a>
|
||||||
our user's privacy but also keep the information in the final e-mail to enable response to complaints and abuse (the
|
This task was the initial reason to develop this library: remove the X-Originating-IP header to preserve our user's
|
||||||
original header is usually provided in these cases). Add this snippet to your DATA ACL section in exim:
|
privacy but also keep the information in the final e-mail to enable response to complaints and abuse (the original
|
||||||
|
header is usually provided in these cases). Add this snippet to your DATA ACL section in exim:
|
||||||
|
|
||||||
```
|
```
|
||||||
warn log_message = Removing X-Originating-IP: header
|
warn log_message = Removing X-Originating-IP: header
|
||||||
condition = ${if def:h_X-originating-IP: {1}{0}}
|
condition = ${if def:h_X-originating-IP: {1}{0}}
|
||||||
add_header = X-Orig-IP-PKK: ${dlfunc{/usr/local/lib/libexim-encrypt-dlfunc.so} \
|
add_header = X-Orig-IP-PKK: ${dlfunc{/usr/lib/x86_64-linux-gnu/libexim-encrypt-dlfunc.so} \
|
||||||
{sodium_crypto_box_seal} \
|
{sodium_crypto_box_seal} \
|
||||||
{ktp1OEEItrgvSfpVTtu+ybyNjzuuN8OzCdfrGAJt4j8=} \
|
{ktp1OEEItrgvSfpVTtu+ybyNjzuuN8OzCdfrGAJt4j8=} \
|
||||||
{$h_X-originating-IP:}}
|
{$h_X-originating-IP:}}
|
||||||
add_header = X-Orig-IP-Pass: ${dlfunc{/usr/local/lib/libexim-encrypt-dlfunc.so} \
|
add_header = X-Orig-IP-Pass: ${dlfunc{/usr/lib/x86_64-linux-gnu/libexim-encrypt-dlfunc.so} \
|
||||||
{sodium_crypto_secretbox_encrypt_password} \
|
{sodium_crypto_secretbox_encrypt_password} \
|
||||||
{Insert your password here} \
|
{Insert your password here} \
|
||||||
{$h_X-originating-IP:}}
|
{$h_X-originating-IP:}}
|
||||||
@ -120,3 +148,55 @@ warn log_message = Removing X-Originating-IP: header
|
|||||||
```
|
```
|
||||||
|
|
||||||
Pick one of the `add_header` lines depending on which kind of encryption you want.
|
Pick one of the `add_header` lines depending on which kind of encryption you want.
|
||||||
|
|
||||||
|
### Decryption Tools
|
||||||
|
|
||||||
|
Two additional programs are included:
|
||||||
|
|
||||||
|
* `libexim-encrypt-dlfunc-decrypt-secretbox`
|
||||||
|
* `libexim-encrypt-dlfunc-decrypt-sealedbox`
|
||||||
|
|
||||||
|
They can decrypt strings that were encrypted by the two respective functions. Please refer to their `--help` message
|
||||||
|
(reproduced below) for usage information and to both [test](src/test_libexim-encrypt-dlfunc-decrypt-secretbox.sh)
|
||||||
|
[scripts](src/test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh) for usage examples.
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ libexim-encrypt-dlfunc-decrypt-secretbox -h
|
||||||
|
Usage: libexim-encrypt-dlfunc-decrypt-secretbox [OPTIONS] [CIPHERTEXT]
|
||||||
|
|
||||||
|
Password:
|
||||||
|
-p, --password PASSWORD Decrypt using PASSWORD
|
||||||
|
|
||||||
|
If the environment variable LIBEXIM_PASSWORD is set the password is read from there.
|
||||||
|
Setting a password with -p/--password overwrites this mechanism.
|
||||||
|
|
||||||
|
Select input:
|
||||||
|
-f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)
|
||||||
|
|
||||||
|
Output:
|
||||||
|
-n, --no-newline Do not append a newline to the output
|
||||||
|
|
||||||
|
Password and ciphertext are expected to be base64-encoded (as produced by the library).
|
||||||
|
```
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ libexim-encrypt-dlfunc-decrypt-sealedbox -h
|
||||||
|
Usage: libexim-encrypt-dlfunc-decrypt-sealedbox [OPTIONS] [CIPHERTEXT]
|
||||||
|
|
||||||
|
Secret and public key:
|
||||||
|
-s, --secret-key SECRETKEY Secret key (base64-encoded)
|
||||||
|
-p, --public-key PUBLICKEY Public key (base64-encoded)
|
||||||
|
-S, --secret-key-file FILE Read secret key (raw) from file FILE (use - for stdin)
|
||||||
|
-P, --public-key-file FILE Read public key (raw) from file FILE (use - for stdin)
|
||||||
|
|
||||||
|
The environment variables LIBEXIM_SECRETKEY and LIBEXIM_PUBLICKEY may contain base64-encoded secret/public keys.
|
||||||
|
|
||||||
|
Select input:
|
||||||
|
-f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)
|
||||||
|
|
||||||
|
Output:
|
||||||
|
-n, --no-newline Do not append a newline to the output
|
||||||
|
|
||||||
|
Keys in arguments and environment variables are expected to be base64 encoded (as produced by the library).
|
||||||
|
Keys in files need to be raw bytes with no encoding, ciphertext should always be base64-encoded.
|
||||||
|
```
|
||||||
|
|||||||
41
ci_container/Dockerfile
Normal file
41
ci_container/Dockerfile
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
FROM debian:latest
|
||||||
|
|
||||||
|
LABEL org.opencontainers.image.created=${BUILDTIMESTAMP} \
|
||||||
|
org.opencontainers.image.authors="Heiko Reese <reese@kit.edu>" \
|
||||||
|
org.opencontainers.image.title="exim build container" \
|
||||||
|
org.opencontainers.image.description="A buld environment for exim-encrypt-dlfunc" \
|
||||||
|
org.opencontainers.image.source="https://git.scc.kit.edu/mail/exim-encrypt-dlfunc/" \
|
||||||
|
org.opencontainers.image.licenses="Apache-2.0"
|
||||||
|
|
||||||
|
RUN apt-get update; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install --no-install-recommends -y \
|
||||||
|
build-essential \
|
||||||
|
exim4-daemon-heavy \
|
||||||
|
exim4-dev \
|
||||||
|
gcovr \
|
||||||
|
git \
|
||||||
|
libsodium-dev \
|
||||||
|
openssl \
|
||||||
|
pkg-config \
|
||||||
|
python3-pip \
|
||||||
|
libxml2-utils; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install --no-install-recommends -y \
|
||||||
|
debhelper \
|
||||||
|
debsigs \
|
||||||
|
devscripts \
|
||||||
|
dh-make \
|
||||||
|
git-buildpackage \
|
||||||
|
gpgv1 \
|
||||||
|
meson; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get install --no-install-recommends -y \
|
||||||
|
less \
|
||||||
|
mc \
|
||||||
|
openssh-client \
|
||||||
|
vim; \
|
||||||
|
DEBIAN_FRONTEND=noninteractive DEBIAN_PRIORITY=critical apt-get clean; \
|
||||||
|
rm -rf /var/lib/apt/lists/*;
|
||||||
|
|
||||||
|
RUN pip3 install --upgrade meson ninja; \
|
||||||
|
rm -rf ~/.cache/pip/*;
|
||||||
|
|
||||||
|
WORKDIR /mnt/
|
||||||
17
ci_container/README.md
Normal file
17
ci_container/README.md
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# How to build and use these images
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
* [buildah](https://buildah.io/)
|
||||||
|
* [podman](https://podman.io/)
|
||||||
|
|
||||||
|
## Build and upload
|
||||||
|
|
||||||
|
Run `build.sh` with the required parameters:
|
||||||
|
|
||||||
|
* `-r`: Registry to upload to; set to `none` to skip uploading
|
||||||
|
* `-u`: Username for registry (optional if previous cached login exists)
|
||||||
|
* `-p`: Passwort for registry (optional if previous cached login exists)
|
||||||
|
* `-b`: Image basename (optional; defaults to `exim-encrypt-dlfunc-build`)
|
||||||
|
* `-t`: Image tag (optional, defaults to `latest`)
|
||||||
|
|
||||||
76
ci_container/build.sh
Executable file
76
ci_container/build.sh
Executable file
@ -0,0 +1,76 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck disable=SC1004
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
images=('debian:buster|buster' 'debian:bullseye|bullseye' 'ubuntu:focal|focal')
|
||||||
|
|
||||||
|
BASENAME='exim-encrypt-dlfunc-build'
|
||||||
|
TAG='latest'
|
||||||
|
REGISTRY='localhost:5000'
|
||||||
|
USERNAME='nobody'
|
||||||
|
PASSWORD='password'
|
||||||
|
|
||||||
|
while getopts "r:u:p:b:t:" OPTION; do
|
||||||
|
case $OPTION in
|
||||||
|
r)
|
||||||
|
REGISTRY="${OPTARG}"
|
||||||
|
;;
|
||||||
|
u)
|
||||||
|
USERNAME="${OPTARG}"
|
||||||
|
;;
|
||||||
|
p)
|
||||||
|
PASSWORD="${OPTARG}"
|
||||||
|
;;
|
||||||
|
b)
|
||||||
|
BASENAME="${OPTARG}"
|
||||||
|
;;
|
||||||
|
t)
|
||||||
|
TAG="${OPTARG}"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [OPTIONS]"
|
||||||
|
echo
|
||||||
|
echo "Options:"
|
||||||
|
echo " -r <registry> (default: \"${REGISTRY}\")"
|
||||||
|
echo " -u <username> (default: \"${USERNAME}\")"
|
||||||
|
echo " -p <password> (default: \"${PASSWORD}\")"
|
||||||
|
echo " -b <basename> (default: \"${BASENAME}\")"
|
||||||
|
echo " -t <tag> (default: \"${TAG}\")"
|
||||||
|
echo
|
||||||
|
exit 127
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
REGHOST="$(echo "${REGISTRY}" | cut -d/ -f1)"
|
||||||
|
if [ "${REGISTRY}" != "none" ]; then
|
||||||
|
echo "🔑 Logging into »${REGHOST}«"
|
||||||
|
if ! buildah login --get-login "${REGHOST}" > /dev/null 2> /dev/null; then
|
||||||
|
buildah login --password "${PASSWORD}" --username "${USERNAME}" "${REGHOST}"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
for i in "${images[@]}"; do
|
||||||
|
basectr=$(echo "${i}" | cut -d'|' -f1)
|
||||||
|
name=$(echo "${i}" | cut -d'|' -f2)
|
||||||
|
|
||||||
|
IMAGENAME="${BASENAME}-${name}"
|
||||||
|
IIDFILE=".${IMAGENAME}.iid"
|
||||||
|
IMAGETAG="${BASENAME}-${name}:${TAG}"
|
||||||
|
TARGET="${REGISTRY}/${IMAGETAG}"
|
||||||
|
echo "🔨 Assembling »${IMAGENAME}«"
|
||||||
|
buildah build-using-dockerfile \
|
||||||
|
--from "${basectr}" \
|
||||||
|
--format docker \
|
||||||
|
--iidfile "${IIDFILE}" \
|
||||||
|
--tag "${IMAGETAG}"
|
||||||
|
--build-arg BUILDTIMESTAMP="`date --iso-8601=seconds`"
|
||||||
|
|
||||||
|
if [ "${REGISTRY}" != "none" ]; then
|
||||||
|
echo "🚀 Pushing »${IMAGETAG}« to »${TARGET}«"
|
||||||
|
buildah push "${IMAGETAG}" "${TARGET}"
|
||||||
|
echo "✅ Finished »${BASENAME}«"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -f "${IIDFILE}"
|
||||||
|
done
|
||||||
5
debian/changelog
vendored
Normal file
5
debian/changelog
vendored
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
exim-encrypt-dlfunc (0.2.0) unstable; urgency=medium
|
||||||
|
|
||||||
|
* Initial Release.
|
||||||
|
|
||||||
|
-- Heiko Reese <heiko.reese@kit.edu> Sun, 22 Aug 2021 20:00:57 +0000
|
||||||
19
debian/control
vendored
Normal file
19
debian/control
vendored
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
Source: exim-encrypt-dlfunc
|
||||||
|
Priority: optional
|
||||||
|
Maintainer: Heiko Reese <heiko.reese@kit.edu>
|
||||||
|
Build-Depends: debhelper-compat (= 12), build-essential, exim4-dev, libsodium-dev, meson, pkg-config, openssl, python3-pip
|
||||||
|
Standards-Version: 4.5.1
|
||||||
|
Section: libs
|
||||||
|
Homepage: https://git.scc.kit.edu/mail/exim-encrypt-dlfunc
|
||||||
|
Vcs-Browser: https://git.scc.kit.edu/mail/exim-encrypt-dlfunc
|
||||||
|
Vcs-Git: https://git.scc.kit.edu/mail/exim-encrypt-dlfunc.git
|
||||||
|
Rules-Requires-Root: no
|
||||||
|
|
||||||
|
Package: exim-encrypt-dlfunc
|
||||||
|
Architecture: any
|
||||||
|
Multi-Arch: same
|
||||||
|
Depends: ${shlibs:Depends}, ${misc:Depends}, exim4-daemon-heavy
|
||||||
|
Description: String encryption library for exim4
|
||||||
|
This library provides functions to encrypt and decrypt strings within exim4
|
||||||
|
using either passwords or public/private key pairs. All cryptographic
|
||||||
|
functionality is provides by libsodium.
|
||||||
29
debian/copyright
vendored
Normal file
29
debian/copyright
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||||
|
Upstream-Name: exim-encrypt-dlfunc
|
||||||
|
Upstream-Contact: Heiko Reese <heiko.reese@kit.edu>
|
||||||
|
Source: https://git.scc.kit.edu/mail/exim-encrypt-dlfunc
|
||||||
|
|
||||||
|
Files: *
|
||||||
|
Copyright: 2021 Heiko Reese <heiko.reese@kit.edu>
|
||||||
|
License: Apache-2.0
|
||||||
|
|
||||||
|
Files: debian/*
|
||||||
|
Copyright: 2021 Heiko Reese <heiko.reese@kit.edu>
|
||||||
|
License: Apache-2.0
|
||||||
|
|
||||||
|
License: Apache-2.0
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
.
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
.
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
.
|
||||||
|
On Debian systems, the complete text of the Apache version 2.0 license
|
||||||
|
can be found in "/usr/share/common-licenses/Apache-2.0".
|
||||||
|
|
||||||
25
debian/rules
vendored
Executable file
25
debian/rules
vendored
Executable file
@ -0,0 +1,25 @@
|
|||||||
|
#!/usr/bin/make -f
|
||||||
|
# See debhelper(7) (uncomment to enable)
|
||||||
|
# output every command that modifies files on the build system.
|
||||||
|
export DH_VERBOSE = 1
|
||||||
|
|
||||||
|
|
||||||
|
# see FEATURE AREAS in dpkg-buildflags(1)
|
||||||
|
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
|
||||||
|
|
||||||
|
# see ENVIRONMENT in dpkg-buildflags(1)
|
||||||
|
# package maintainers to append CFLAGS
|
||||||
|
#export DEB_CFLAGS_MAINT_APPEND = -Wall -pedantic
|
||||||
|
# package maintainers to append LDFLAGS
|
||||||
|
#export DEB_LDFLAGS_MAINT_APPEND = -Wl,--as-needed
|
||||||
|
|
||||||
|
|
||||||
|
%:
|
||||||
|
dh $@
|
||||||
|
|
||||||
|
|
||||||
|
# dh_make generated override targets
|
||||||
|
# This is example for Cmake (See https://bugs.debian.org/641051 )
|
||||||
|
#override_dh_auto_configure:
|
||||||
|
# dh_auto_configure -- \
|
||||||
|
# -DCMAKE_LIBRARY_PATH=$(DEB_HOST_MULTIARCH)
|
||||||
86
src/common.c
Normal file
86
src/common.c
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sodium.h>
|
||||||
|
#include "common.h"
|
||||||
|
|
||||||
|
char *read_first_line(const char *filename) {
|
||||||
|
FILE *stream;
|
||||||
|
char *cipherstring;
|
||||||
|
size_t len = 0;
|
||||||
|
ssize_t nread;
|
||||||
|
bool input_is_stdin = false;
|
||||||
|
|
||||||
|
// open file (use stdin for '-')
|
||||||
|
if (!strncmp(filename, "-", 1)) {
|
||||||
|
stream = stdin;
|
||||||
|
input_is_stdin = true;
|
||||||
|
} else {
|
||||||
|
stream = fopen(filename, "r");
|
||||||
|
if (stream == NULL) {
|
||||||
|
fprintf(stderr, "[ERROR] Error opening file %s\n\n", filename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nread = getline(&cipherstring, &len, stream);
|
||||||
|
|
||||||
|
if (nread == -1 && !feof(stream)) {
|
||||||
|
perror("getline: ");
|
||||||
|
}
|
||||||
|
if (input_is_stdin == false) {
|
||||||
|
fclose(stream);
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove trailing newline
|
||||||
|
cipherstring[strcspn(cipherstring, "\r\n")] = 0;
|
||||||
|
|
||||||
|
return cipherstring;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *read_password_file(const char *filename, size_t keysize, size_t *length) {
|
||||||
|
FILE *stream;
|
||||||
|
char *contents;
|
||||||
|
ssize_t nread;
|
||||||
|
bool input_is_stdin = false;
|
||||||
|
|
||||||
|
contents = malloc(keysize + 1);
|
||||||
|
sodium_memzero(contents, keysize + 1);
|
||||||
|
|
||||||
|
// open file (use stdin for '-')
|
||||||
|
if (!strncmp(filename, "-", 1)) {
|
||||||
|
stream = stdin;
|
||||||
|
input_is_stdin = true;
|
||||||
|
} else {
|
||||||
|
stream = fopen(filename, "r");
|
||||||
|
if (stream == NULL) {
|
||||||
|
fprintf(stderr, "[ERROR] Error opening file %s\n\n", filename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nread = fread(contents, sizeof(char), keysize, stream);
|
||||||
|
|
||||||
|
if (nread < 0) {
|
||||||
|
fprintf(stderr, "[ERROR] reading from %s failed\n\n", filename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
} else {
|
||||||
|
*length = (size_t) nread;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (input_is_stdin == false) {
|
||||||
|
fclose(stream);
|
||||||
|
}
|
||||||
|
return contents;
|
||||||
|
}
|
||||||
|
|
||||||
|
int base64_decode_string(const char *input, unsigned char **outstring, size_t *outlen) {
|
||||||
|
size_t input_len = strlen(input);
|
||||||
|
size_t outmaxlen = input_len / 4 * 3;
|
||||||
|
*outstring = malloc(outmaxlen * sizeof(unsigned char));
|
||||||
|
return sodium_base642bin(*outstring, outmaxlen, (const char *) input, input_len,
|
||||||
|
NULL, outlen, NULL, sodium_base64_VARIANT_ORIGINAL);
|
||||||
|
}
|
||||||
14
src/common.h
Normal file
14
src/common.h
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
//
|
||||||
|
// Created by sprawl on 08/09/2021.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef EXIM_ENCRYPT_DLFUNC_COMMON_H
|
||||||
|
#define EXIM_ENCRYPT_DLFUNC_COMMON_H
|
||||||
|
|
||||||
|
char *read_first_line(const char *filename);
|
||||||
|
|
||||||
|
char *read_password_file(const char *filename, size_t keysize, size_t *length);
|
||||||
|
|
||||||
|
int base64_decode_string(const char *input, unsigned char **outstring, size_t *outlen);
|
||||||
|
|
||||||
|
#endif //EXIM_ENCRYPT_DLFUNC_COMMON_H
|
||||||
@ -20,7 +20,7 @@ char *string2hex(unsigned char *input, size_t length) {
|
|||||||
* 1. Add this code to the first “breakpoint”:
|
* 1. Add this code to the first “breakpoint”:
|
||||||
* log_write(0, LOG_MAIN, "pid: %d", getpid()); int busywait = 0; while (busywait == 0) {}
|
* log_write(0, LOG_MAIN, "pid: %d", getpid()); int busywait = 0; while (busywait == 0) {}
|
||||||
* 2. Compile.
|
* 2. Compile.
|
||||||
* 3. Run “exim -be […]” to call the lib; see simple_exim_test.sh for details.
|
* 3. Run “exim -be […]” to call the lib; see test_libexim-encrypt-dlfunc.sh for details.
|
||||||
* 4. Read exim pid from log output. Attach to the looping exim process with “gdb -p PID”
|
* 4. Read exim pid from log output. Attach to the looping exim process with “gdb -p PID”
|
||||||
* 5. Prepare breakpoints, watches, etc. Set busywait to 1 and continue.
|
* 5. Prepare breakpoints, watches, etc. Set busywait to 1 and continue.
|
||||||
*/
|
*/
|
||||||
207
src/libexim-encrypt-dlfunc-decrypt-sealedbox.c
Normal file
207
src/libexim-encrypt-dlfunc-decrypt-sealedbox.c
Normal file
@ -0,0 +1,207 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sodium.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "common.c"
|
||||||
|
|
||||||
|
#define ENVVAR_SK_NAME "LIBEXIM_SECRETKEY"
|
||||||
|
#define ENVVAR_PK_NAME "LIBEXIM_PUBLICKEY"
|
||||||
|
|
||||||
|
void print_usage(char *progname) {
|
||||||
|
printf("Usage: %s [OPTIONS] [CIPHERTEXT]\n\n", progname);
|
||||||
|
printf("Secret and public key:\n");
|
||||||
|
printf(" -s, --secret-key SECRETKEY Secret key (base64-encoded)\n");
|
||||||
|
printf(" -p, --public-key PUBLICKEY Public key (base64-encoded)\n");
|
||||||
|
printf(" -S, --secret-key-file FILE Read secret key (raw) from file FILE (use - for stdin)\n");
|
||||||
|
printf(" -P, --public-key-file FILE Read public key (raw) from file FILE (use - for stdin)\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("The environment variables %s and %s may contain base64-encoded secret/public keys. \n", ENVVAR_SK_NAME,
|
||||||
|
ENVVAR_PK_NAME);
|
||||||
|
printf("\n");
|
||||||
|
printf("Select input:\n");
|
||||||
|
printf(" -f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Output:\n");
|
||||||
|
printf(" -n, --no-newline Do not append a newline to the output\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Keys in arguments and environment variables are expected to be base64 encoded (as produced by the library).\n");
|
||||||
|
printf("Keys in files need to be raw bytes with no encoding, ciphertext should always be base64-encoded.\n");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NONE = 0,
|
||||||
|
SK = 1,
|
||||||
|
PK = 2,
|
||||||
|
SKENV = 4,
|
||||||
|
PKENV = 8,
|
||||||
|
SKFILE = 16,
|
||||||
|
PKFILE = 32,
|
||||||
|
INFILE = 64,
|
||||||
|
INSTRING = 128
|
||||||
|
} seen_sb_args;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
char *prog_basename = basename(argv[0]);
|
||||||
|
int opt;
|
||||||
|
unsigned char *secretkey, *publickey;
|
||||||
|
size_t secretkey_len = 0, publickey_len = 0;
|
||||||
|
char *b64cipherstring;
|
||||||
|
unsigned char *cipherstring;
|
||||||
|
size_t cipherstring_len;
|
||||||
|
bool add_newline = true;
|
||||||
|
|
||||||
|
seen_sb_args publickey_mode = NONE;
|
||||||
|
seen_sb_args secretkey_mode = NONE;
|
||||||
|
seen_sb_args input = NONE;
|
||||||
|
|
||||||
|
if (sodium_init() < 0) {
|
||||||
|
fputs("Unable to initialize libsodium", stderr);
|
||||||
|
exit(128);
|
||||||
|
}
|
||||||
|
|
||||||
|
// define arguments
|
||||||
|
const char *shortargs = "s:p:S:P:f:nh";
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"secret-key", required_argument, NULL, 's'},
|
||||||
|
{"public-key", required_argument, NULL, 'p'},
|
||||||
|
{"secret-key-file", required_argument, NULL, 'S'},
|
||||||
|
{"public-key-file", required_argument, NULL, 'P'},
|
||||||
|
{"infile", required_argument, NULL, 'f'},
|
||||||
|
{"no-newline", required_argument, NULL, 'n'},
|
||||||
|
{"help", no_argument, NULL, 'h'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
// check environment for LIBEXIM_SECRETKEY variable
|
||||||
|
char *sk_env = getenv(ENVVAR_SK_NAME);
|
||||||
|
if (sk_env != NULL && strlen(sk_env) > 0) {
|
||||||
|
if (base64_decode_string(sk_env, &secretkey, &secretkey_len) != 0) {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to base64-decode secret key.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
secretkey_mode |= SKENV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check environment for LIBEXIM_PUBLICKEY variable
|
||||||
|
char *pk_env = getenv(ENVVAR_PK_NAME);
|
||||||
|
if (pk_env != NULL && strlen(pk_env) > 0) {
|
||||||
|
if (base64_decode_string(pk_env, &publickey, &publickey_len) != 0) {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to base64-decode public key.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
publickey_mode |= PKENV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse arguments
|
||||||
|
int long_index = 0;
|
||||||
|
while ((opt = getopt_long(argc, argv, shortargs,
|
||||||
|
long_options, &long_index)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 's':
|
||||||
|
if (base64_decode_string(optarg, &secretkey, &secretkey_len) != 0) {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to base64-decode secret key.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
secretkey_mode |= SK;
|
||||||
|
break;
|
||||||
|
case 'p':
|
||||||
|
if (base64_decode_string(optarg, &publickey, &publickey_len) != 0) {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to base64-decode public key.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
publickey_mode |= PK;
|
||||||
|
break;
|
||||||
|
case 'S':
|
||||||
|
secretkey = (unsigned char *) read_password_file(optarg, crypto_box_SECRETKEYBYTES, &secretkey_len);
|
||||||
|
secretkey_mode |= SKFILE;
|
||||||
|
break;
|
||||||
|
case 'P':
|
||||||
|
publickey = (unsigned char *) read_password_file(optarg, crypto_box_PUBLICKEYBYTES, &publickey_len);
|
||||||
|
publickey_mode |= PKFILE;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
b64cipherstring = read_first_line(optarg);
|
||||||
|
input |= INFILE;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
add_newline = false;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
print_usage(prog_basename);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read first non-option argument as ciphertext if present
|
||||||
|
if (optind < argc) {
|
||||||
|
size_t b64cipherstring_len = strlen(argv[optind]);
|
||||||
|
b64cipherstring = malloc(b64cipherstring_len + 1);
|
||||||
|
sodium_memzero(b64cipherstring, b64cipherstring_len + 1);
|
||||||
|
strncpy(b64cipherstring, argv[optind], b64cipherstring_len);
|
||||||
|
input |= INSTRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a secret key was provided
|
||||||
|
if (secretkey_mode == NONE) {
|
||||||
|
fprintf(stderr, "[ERROR] Please specify a secret key.\n\n");
|
||||||
|
print_usage(prog_basename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// check if the secret key has the correct size
|
||||||
|
if (secretkey_len != crypto_box_SECRETKEYBYTES) {
|
||||||
|
fprintf(stderr, "[ERROR] Secret key has wrong size %zu; expected %d.\n\n", secretkey_len,
|
||||||
|
crypto_box_SECRETKEYBYTES);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a public key was provided
|
||||||
|
if (publickey_mode == NONE) {
|
||||||
|
fprintf(stderr, "[ERROR] Please specify a public key.\n\n");
|
||||||
|
print_usage(prog_basename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// check if the public key has the correct size
|
||||||
|
if (publickey_len != crypto_box_PUBLICKEYBYTES) {
|
||||||
|
fprintf(stderr, "[ERROR] Secret key has wrong size %zu; expected %d.\n\n", publickey_len,
|
||||||
|
crypto_box_PUBLICKEYBYTES);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a ciphertext was provided
|
||||||
|
if (input == NONE) {
|
||||||
|
fprintf(stderr, "[ERROR] Please specify a ciphertext source.\n\n");
|
||||||
|
print_usage(prog_basename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
// base64-decode ciphertext
|
||||||
|
if (base64_decode_string(b64cipherstring, &cipherstring, &cipherstring_len) != 0) {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to base64-decode ciphertext.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
free(b64cipherstring);
|
||||||
|
|
||||||
|
// prepare buffer for cleartext
|
||||||
|
size_t cleartext_len = cipherstring_len - crypto_box_SEALBYTES;
|
||||||
|
unsigned char *cleartext = (unsigned char *) malloc(cleartext_len + 1);
|
||||||
|
sodium_memzero(cleartext, cleartext_len + 1);
|
||||||
|
|
||||||
|
// decrypt message
|
||||||
|
if (crypto_box_seal_open(cleartext, cipherstring, cipherstring_len, publickey, secretkey) != 0) {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to decrypt ciphertext.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// print cleartext to stdout
|
||||||
|
if (add_newline == true) {
|
||||||
|
fprintf(stdout, "%s\n", (const char *) cleartext);
|
||||||
|
} else {
|
||||||
|
fprintf(stdout, "%s", (const char *) cleartext);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cleartext);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
163
src/libexim-encrypt-dlfunc-decrypt-secretbox.c
Normal file
163
src/libexim-encrypt-dlfunc-decrypt-secretbox.c
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
#define _GNU_SOURCE
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <sodium.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "common.c"
|
||||||
|
|
||||||
|
#define ENVVAR_PASSWORD_NAME "LIBEXIM_PASSWORD"
|
||||||
|
|
||||||
|
void print_usage(char *progname) {
|
||||||
|
printf("Usage: %s [OPTIONS] [CIPHERTEXT]\n\n", progname);
|
||||||
|
printf("Password:\n");
|
||||||
|
printf(" -p, --password PASSWORD Decrypt using PASSWORD\n");
|
||||||
|
printf("\n");
|
||||||
|
printf(" If the environment variable LIBEXIM_PASSWORD is set the password is read from there.\n");
|
||||||
|
printf(" Setting a password with -p/--password overwrites this mechanism.\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Select input:\n");
|
||||||
|
printf(" -f, --infile FILE Decrypt contents of the first line of file FILE (use - for stdin)\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Output:\n");
|
||||||
|
printf(" -n, --no-newline Do not append a newline to the output\n");
|
||||||
|
printf("\n");
|
||||||
|
printf("Password and ciphertext are expected to be base64-encoded (as produced by the library).\n");
|
||||||
|
printf("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
NONE = 0,
|
||||||
|
PASSARG = 1,
|
||||||
|
PASSENV = 2,
|
||||||
|
INSTRING = 4,
|
||||||
|
INFILE = 8
|
||||||
|
} seen_args;
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
char *prog_basename = basename(argv[0]);
|
||||||
|
int opt;
|
||||||
|
char *cipherstring;
|
||||||
|
size_t password_len;
|
||||||
|
char *password;
|
||||||
|
char *password_env;
|
||||||
|
bool add_newline = true;
|
||||||
|
|
||||||
|
seen_args mode = NONE;
|
||||||
|
seen_args input = NONE;
|
||||||
|
|
||||||
|
if (sodium_init() < 0) {
|
||||||
|
fputs("Unable to initialize libsodium", stderr);
|
||||||
|
exit(128);
|
||||||
|
}
|
||||||
|
|
||||||
|
// define arguments
|
||||||
|
const char *shortargs = "p:f:nh";
|
||||||
|
static struct option long_options[] = {
|
||||||
|
{"password", required_argument, NULL, 'p'},
|
||||||
|
{"infile", required_argument, NULL, 'f'},
|
||||||
|
{"no-newline", no_argument, NULL, 'n'},
|
||||||
|
{"help", no_argument, NULL, 'h'},
|
||||||
|
{0, 0, 0, 0}
|
||||||
|
};
|
||||||
|
|
||||||
|
// check environment for LIBEXIM_PASSWORD
|
||||||
|
password_env = getenv(ENVVAR_PASSWORD_NAME);
|
||||||
|
if (password_env != NULL && strlen(password_env) > 0) {
|
||||||
|
password = password_env;
|
||||||
|
password_len = strlen(password);
|
||||||
|
mode |= PASSENV;
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse arguments
|
||||||
|
int long_index = 0;
|
||||||
|
while ((opt = getopt_long(argc, argv, shortargs,
|
||||||
|
long_options, &long_index)) != -1) {
|
||||||
|
switch (opt) {
|
||||||
|
case 'p':
|
||||||
|
password_len = strlen((const char *) optarg);
|
||||||
|
password = malloc(password_len + 1);
|
||||||
|
strncpy(password, optarg, password_len);
|
||||||
|
mode |= PASSARG;
|
||||||
|
break;
|
||||||
|
case 'f':
|
||||||
|
cipherstring = read_first_line(optarg);
|
||||||
|
input |= INFILE;
|
||||||
|
break;
|
||||||
|
case 'n':
|
||||||
|
add_newline = false;
|
||||||
|
break;
|
||||||
|
case 'h':
|
||||||
|
print_usage(prog_basename);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// read first non-option argument as ciphertext if present
|
||||||
|
if (optind < argc) {
|
||||||
|
size_t cipherstring_len = strlen(argv[optind]) + 1;
|
||||||
|
cipherstring = malloc(cipherstring_len + 1);
|
||||||
|
strncpy(cipherstring, argv[optind], cipherstring_len);
|
||||||
|
input |= INSTRING;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if a password was provided
|
||||||
|
if (mode == NONE) {
|
||||||
|
fprintf(stderr, "[ERROR] Please specify a password.\n\n");
|
||||||
|
print_usage(prog_basename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fail if neither argument nor filename for ciphertext is present
|
||||||
|
if (input == NONE) {
|
||||||
|
fprintf(stderr, "[ERROR] Please specify a ciphertext source.\n\n");
|
||||||
|
print_usage(prog_basename);
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Derive key from the password.
|
||||||
|
unsigned char keybytes[crypto_secretbox_KEYBYTES];
|
||||||
|
sodium_memzero(keybytes, crypto_secretbox_KEYBYTES);
|
||||||
|
crypto_generichash(keybytes, crypto_secretbox_KEYBYTES,
|
||||||
|
(unsigned char *) password, password_len, NULL, 0);
|
||||||
|
|
||||||
|
// base64-decode input
|
||||||
|
unsigned char *ciphertext;
|
||||||
|
size_t ciphertext_len;
|
||||||
|
if (base64_decode_string(cipherstring, &ciphertext, &ciphertext_len) != 0) {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to base64-decode ciphertext.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// extract nonce
|
||||||
|
unsigned char nonce[crypto_secretbox_NONCEBYTES];
|
||||||
|
memcpy(nonce, ciphertext, crypto_secretbox_NONCEBYTES);
|
||||||
|
|
||||||
|
// prepare buffer for cleartext
|
||||||
|
if (ciphertext_len < crypto_secretbox_NONCEBYTES + crypto_secretbox_MACBYTES) {
|
||||||
|
fprintf(stderr, "[ERROR] Ciphertext is too small to contain any data.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
size_t cleartext_len = ciphertext_len - crypto_secretbox_NONCEBYTES - crypto_secretbox_MACBYTES;
|
||||||
|
unsigned char *cleartext = (unsigned char *) malloc(cleartext_len + 1);
|
||||||
|
sodium_memzero(cleartext, cleartext_len + 1);
|
||||||
|
|
||||||
|
// decrypt
|
||||||
|
if (crypto_secretbox_open_easy(cleartext, &ciphertext[crypto_secretbox_NONCEBYTES],
|
||||||
|
ciphertext_len - crypto_secretbox_NONCEBYTES, nonce, keybytes) == 0) {
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "[ERROR] Unable to decrypt message.\n\n");
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
// print cleartext to stdout
|
||||||
|
if (add_newline == true) {
|
||||||
|
fprintf(stdout, "%s\n", (const char *) cleartext);
|
||||||
|
} else {
|
||||||
|
fprintf(stdout, "%s", (const char *) cleartext);
|
||||||
|
}
|
||||||
|
|
||||||
|
free(cleartext);
|
||||||
|
exit(EXIT_SUCCESS);
|
||||||
|
}
|
||||||
@ -2,38 +2,42 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sodium.h>
|
#include <sodium.h>
|
||||||
|
|
||||||
bool key_contains_zero(unsigned char *key, unsigned int keylen)
|
//void
|
||||||
{
|
//dump_key_as_c_code(FILE * f, const char *name, unsigned char *key,
|
||||||
bool has_zero = false;
|
// unsigned int keylen)
|
||||||
for (unsigned int i = 0; i < keylen; i++) {
|
//{
|
||||||
if (key[i] == 0) {
|
// fprintf(f, "const unsigned char %s[] = { ", name);
|
||||||
has_zero = true;
|
// for (unsigned int i = 0; i < keylen; i++) {
|
||||||
}
|
// fprintf(f, "0x%02x", key[i]);
|
||||||
}
|
// if (i < keylen - 1) {
|
||||||
return has_zero;
|
// fprintf(f, ", ");
|
||||||
}
|
// }
|
||||||
|
// }
|
||||||
|
// fprintf(f, " };\n");
|
||||||
|
// fprintf(f, "const unsigned int %s_length = %d;\n", name, keylen);
|
||||||
|
//}
|
||||||
|
|
||||||
|
//void create_secretbox_key(const char *filebase, const char *varname) {
|
||||||
|
// unsigned char key[crypto_secretbox_KEYBYTES];
|
||||||
|
//
|
||||||
|
// crypto_secretbox_keygen(key);
|
||||||
|
//
|
||||||
|
// char key_filename[4096];
|
||||||
|
// char key_varname[4096];
|
||||||
|
//
|
||||||
|
// sprintf(key_filename, "%s_secretbox", filebase);
|
||||||
|
// sprintf(key_varname, "%s_key", varname);
|
||||||
|
//
|
||||||
|
// write_key_files(key_filename, key_varname, key, crypto_secretbox_KEYBYTES);
|
||||||
|
//}
|
||||||
|
|
||||||
void
|
void
|
||||||
dump_key_as_c_code(FILE * f, const char *name, unsigned char *key,
|
dump_key_as_exim_config(FILE *f, const char *name, unsigned char *key,
|
||||||
unsigned int keylen)
|
unsigned int keylen) {
|
||||||
{
|
|
||||||
fprintf(f, "const unsigned char %s[] = { ", name);
|
|
||||||
for (unsigned int i = 0; i < keylen; i++) {
|
|
||||||
fprintf(f, "0x%02x", key[i]);
|
|
||||||
if (i < keylen - 1) {
|
|
||||||
fprintf(f, ", ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fprintf(f, " };\n");
|
|
||||||
fprintf(f, "const unsigned int %s_length = %d;\n", name, keylen);
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
|
||||||
dump_key_as_exim_config(FILE * f, const char *name, unsigned char *key,
|
|
||||||
unsigned int keylen)
|
|
||||||
{
|
|
||||||
// write a comment with C variable declaration
|
// write a comment with C variable declaration
|
||||||
fprintf(f, "# const unsigned char %s[%d] = { ", name, keylen);
|
fprintf(f, "# const unsigned char %s[%d] = { ", name, keylen);
|
||||||
for (unsigned int i = 0; i < keylen; i++) {
|
for (unsigned int i = 0; i < keylen; i++) {
|
||||||
@ -75,6 +79,9 @@ write_key_files(const char *filebase, const char *varname,
|
|||||||
sprintf(exim_filename, "%s_exim.conf", filebase);
|
sprintf(exim_filename, "%s_exim.conf", filebase);
|
||||||
sprintf(raw_filename, "%s.raw", filebase);
|
sprintf(raw_filename, "%s.raw", filebase);
|
||||||
|
|
||||||
|
// set restrictive umask (access to user only)
|
||||||
|
mode_t original_umask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
|
||||||
|
|
||||||
// open exim config snippet file
|
// open exim config snippet file
|
||||||
f = fopen(exim_filename, "w+");
|
f = fopen(exim_filename, "w+");
|
||||||
if (f == NULL) {
|
if (f == NULL) {
|
||||||
@ -101,6 +108,8 @@ write_key_files(const char *filebase, const char *varname,
|
|||||||
// close raw file
|
// close raw file
|
||||||
fclose(f);
|
fclose(f);
|
||||||
|
|
||||||
|
// restore original umask
|
||||||
|
umask(original_umask);
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_cryptobox_keys(const char *filebase, const char *varname)
|
void create_cryptobox_keys(const char *filebase, const char *varname)
|
||||||
@ -127,20 +136,6 @@ void create_cryptobox_keys(const char *filebase, const char *varname)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void create_secretbox_key(const char *filebase, const char *varname) {
|
|
||||||
unsigned char key[crypto_secretbox_KEYBYTES];
|
|
||||||
|
|
||||||
crypto_secretbox_keygen(key);
|
|
||||||
|
|
||||||
char key_filename[4096];
|
|
||||||
char key_varname[4096];
|
|
||||||
|
|
||||||
sprintf(key_filename, "%s_secretbox", filebase);
|
|
||||||
sprintf(key_varname, "%s_key", varname);
|
|
||||||
|
|
||||||
write_key_files(key_filename, key_varname, key, crypto_secretbox_KEYBYTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(void) {
|
int main(void) {
|
||||||
if (sodium_init() < 0) {
|
if (sodium_init() < 0) {
|
||||||
fputs("Unable to initialize libsodium", stderr);
|
fputs("Unable to initialize libsodium", stderr);
|
||||||
@ -64,7 +64,7 @@ int sodium_crypto_secretbox_encrypt_password(uschar **yield, int argc, uschar *a
|
|||||||
|
|
||||||
/*
|
/*
|
||||||
* Derive a key from the password using a generic hash.
|
* Derive a key from the password using a generic hash.
|
||||||
* This operations needs to be fast (exim holds no state, this might be called multiple times per email).
|
* This operation needs to be fast (exim holds no state, this might be called multiple times per email).
|
||||||
* Collisions avoidance or brute force attacks are not a concern here.
|
* Collisions avoidance or brute force attacks are not a concern here.
|
||||||
*/
|
*/
|
||||||
unsigned char keybytes[crypto_secretbox_KEYBYTES];
|
unsigned char keybytes[crypto_secretbox_KEYBYTES];
|
||||||
|
|||||||
@ -1,6 +1,14 @@
|
|||||||
configure_file(output: 'config.h', configuration: conf_data)
|
configure_file(output: 'config.h', configuration: conf_data)
|
||||||
|
|
||||||
executable('generate_encryption_keys', 'generate_encryption_keys.c',
|
executable('libexim-encrypt-dlfunc-genkeys', 'libexim-encrypt-dlfunc-genkeys.c',
|
||||||
|
dependencies : [ sodium_deps ],
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
executable('libexim-encrypt-dlfunc-decrypt-sealedbox', 'libexim-encrypt-dlfunc-decrypt-sealedbox.c',
|
||||||
|
dependencies : [ sodium_deps ],
|
||||||
|
install: true)
|
||||||
|
|
||||||
|
executable('libexim-encrypt-dlfunc-decrypt-secretbox', 'libexim-encrypt-dlfunc-decrypt-secretbox.c',
|
||||||
dependencies : [ sodium_deps ],
|
dependencies : [ sodium_deps ],
|
||||||
install: true)
|
install: true)
|
||||||
|
|
||||||
@ -8,7 +16,14 @@ shared_library('exim-encrypt-dlfunc', 'libexim-encrypt-dlfunc.c',
|
|||||||
dependencies : [ sodium_deps ],
|
dependencies : [ sodium_deps ],
|
||||||
install: true)
|
install: true)
|
||||||
|
|
||||||
|
test('libexim-encrypt-dlfunc',
|
||||||
|
find_program('test_libexim-encrypt-dlfunc.sh'),
|
||||||
|
protocol: 'tap')
|
||||||
|
|
||||||
simple_exim_test = find_program('simple_exim_test.sh')
|
test('decrypt-secretbox',
|
||||||
test('simple test', simple_exim_test)
|
find_program('test_libexim-encrypt-dlfunc-decrypt-secretbox.sh'),
|
||||||
|
protocol: 'tap')
|
||||||
|
|
||||||
|
test('decrypt-sealedbox',
|
||||||
|
find_program('test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh'),
|
||||||
|
protocol: 'tap')
|
||||||
@ -1,37 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
PATH=/sbin:/usr/sbin:$PATH
|
|
||||||
|
|
||||||
# copy to /tmp to keep call to exim under 256 chars (prevent problems on Ubuntu)
|
|
||||||
install -t /tmp src/libexim-encrypt-dlfunc.so
|
|
||||||
|
|
||||||
LIB=/tmp/libexim-encrypt-dlfunc.so
|
|
||||||
CLEARTEXT="127.88.99.23" # keep short; see above
|
|
||||||
PASSWORD="`openssl rand -base64 32`"
|
|
||||||
|
|
||||||
CIPHERTEXT=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_encrypt_password}{${PASSWORD}}{${CLEARTEXT}}}")
|
|
||||||
DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_decrypt_password}{${PASSWORD}}{${CIPHERTEXT}}}")
|
|
||||||
|
|
||||||
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
|
|
||||||
echo "secretbox test successful"
|
|
||||||
else
|
|
||||||
echo "secretbox test unsuccessful"
|
|
||||||
exit 127
|
|
||||||
fi
|
|
||||||
|
|
||||||
# { 0xb6, 0x01, 0x45, 0x20, 0x9f, 0x55, 0x06, 0x74, 0x29, 0x71, 0x7b, 0x5e, 0xa9, 0x68, 0x60, 0x5e, 0x81, 0x1a, 0x54, 0x6b, 0xc9, 0x80, 0x97, 0x78, 0x41, 0xc6, 0x20, 0xae, 0x66, 0x9f, 0xd9, 0x53 };
|
|
||||||
PK="tgFFIJ9VBnQpcXteqWhgXoEaVGvJgJd4QcYgrmaf2VM="
|
|
||||||
# { 0x95, 0x8d, 0x45, 0xef, 0x45, 0x6a, 0xc1, 0xef, 0xae, 0x0a, 0x7e, 0x1c, 0xcc, 0x67, 0x70, 0xc8, 0x67, 0x6b, 0xd1, 0x62, 0xd4, 0x59, 0xd9, 0x23, 0xfc, 0x6a, 0xb7, 0xf6, 0x6d, 0xa4, 0xdc, 0xfd };
|
|
||||||
SK="lY1F70Vqwe+uCn4czGdwyGdr0WLUWdkj/Gq39m2k3P0="
|
|
||||||
|
|
||||||
CIPHERTEXT=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal}{${PK}}{${CLEARTEXT}}}")
|
|
||||||
DECRYPTED=$(exim -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal_open}{${SK}}{${PK}}{${CIPHERTEXT}}}")
|
|
||||||
|
|
||||||
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
|
|
||||||
echo "sealed_box test successful"
|
|
||||||
else
|
|
||||||
echo "sealed_box test unsuccessful"
|
|
||||||
exit 128
|
|
||||||
fi
|
|
||||||
46
src/test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh
Executable file
46
src/test_libexim-encrypt-dlfunc-decrypt-sealedbox.sh
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
# this script implements the TAP protocol (https://testanything.org)
|
||||||
|
echo 1..3
|
||||||
|
|
||||||
|
TEST_PUBLICKEY='z7+GlUWgoiXJ4VK6cdLikCF7M6Mj9i4eXNE6Jh1m9yw='
|
||||||
|
TEST_SECRETKEY='h5C/V2nzhILRmJ6UZrNK/6G8Xc4KWzLq/Qr8Xj42jus='
|
||||||
|
TEST_CLEARTEXT='There is nothing in the middle of the road but a yellow stripe and dead armadillos. ~ Jim Hightower'
|
||||||
|
TEST_CIPHERTEXT='+UgXgarCuX3dUobgt8rRnjnxPNWHpsw98GjLZy8+2m5e/v9K/acMq+0UsFW7lwAZIRqj1F55n78y73Y6XCBEVSt8G6nntV8WuDYlr1BHcBIXNr5toUbE+CxtLoGqfD3c3nw1NkJDO1NYGzK/cG43TEEBLrQJCRLRBXOZmxG6ugFo4FtYl297/B1xNtkd9IR4TY5C'
|
||||||
|
CIPHERTEXT_FILE="$(mktemp)"
|
||||||
|
TEST_PUBLICKEY_FILE="$(mktemp)"
|
||||||
|
TEST_SECRETKEY_FILE="$(mktemp)"
|
||||||
|
echo -n "${TEST_CIPHERTEXT}" > "${CIPHERTEXT_FILE}"
|
||||||
|
echo -n "${TEST_PUBLICKEY}" | base64 -d > "${TEST_PUBLICKEY_FILE}"
|
||||||
|
echo -n "${TEST_SECRETKEY}" | base64 -d > "${TEST_SECRETKEY_FILE}"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "${CIPHERTEXT_FILE}" "${TEST_PUBLICKEY_FILE}" "${TEST_SECRETKEY_FILE}"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
export LIBEXIM_PUBLICKEY="${TEST_PUBLICKEY}"
|
||||||
|
export LIBEXIM_SECRETKEY="${TEST_SECRETKEY}"
|
||||||
|
|
||||||
|
DECRYPTED="$(src/libexim-encrypt-dlfunc-decrypt-sealedbox "${TEST_CIPHERTEXT}")"
|
||||||
|
if [ "${DECRYPTED}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 1 - decrypt commandline argument with keys from environment successful"
|
||||||
|
else
|
||||||
|
echo "not ok 1 - decrypt commandline argument with keys from environment unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export -n LIBEXIM_PUBLICKEY LIBEXIM_SECRETKEY
|
||||||
|
|
||||||
|
DECRYPTED="$(src/libexim-encrypt-dlfunc-decrypt-sealedbox --secret-key "${TEST_SECRETKEY}" --public-key "${TEST_PUBLICKEY}" --infile "${CIPHERTEXT_FILE}")"
|
||||||
|
if [ "${DECRYPTED}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 2 - decrypt file contents with keys from commandline"
|
||||||
|
else
|
||||||
|
echo "not ok 2 - decrypt file contents with keys from commandline"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DECRYPTED="$(src/libexim-encrypt-dlfunc-decrypt-sealedbox --secret-key-file "${TEST_SECRETKEY_FILE}" --public-key-file "${TEST_PUBLICKEY_FILE}" --infile - < "${CIPHERTEXT_FILE}")"
|
||||||
|
if [ "${DECRYPTED}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 3 - decrypt stdin contents with keys from files"
|
||||||
|
else
|
||||||
|
echo "not ok 3 - decrypt stdin contents with keys from files"
|
||||||
|
fi
|
||||||
57
src/test_libexim-encrypt-dlfunc-decrypt-secretbox.sh
Executable file
57
src/test_libexim-encrypt-dlfunc-decrypt-secretbox.sh
Executable file
@ -0,0 +1,57 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck disable=SC2034
|
||||||
|
# this script implements the TAP protocol (https://testanything.org)
|
||||||
|
echo 1..5
|
||||||
|
|
||||||
|
TEST_PASSWORD='be6rahqu3bukee3Aengohgoopheeyis5'
|
||||||
|
TEST_CLEARTEXT='The great thing about attackers is that there are so many to choose from! - Daniel J. Bernstein'
|
||||||
|
TEST_CIPHERTEXT01='K+TOzrbkni7wydNvF1gMRwZWQPNnNIXRG9iQgkFhszBu8ImqIrAK4wWWP02UmclITi8DZbr3sg/EVWurDzAYK+pjkcDAa78glz4qXIqrPbYvEIEHPEExFzCtwi5hqOR+KF7tsqbPvdAOIqwf/2KBomX0GS1I/1CxQMrbJd1VgXc51M4hI0I8'
|
||||||
|
TEST_CIPHERTEXT02='lAod5UhfW6fQCxd4PSktnrzwyWzcw05Svio5XqPOr/p/Ts4Pr0eEjj2TgmT2K85T2xrxCiqmE/OUcODRldEWeSqBSxx0Z6PzXqOzz5ZL6Iq1tggjihMydGz9mNS4jRF9f52k5t2i7xFrMMCRrfq/rer/ngp1h3pposCds+OmX0u+1f4Urj0b'
|
||||||
|
CIPHERTEXT_FILE01="$(mktemp)"
|
||||||
|
CIPHERTEXT_FILE02="$(mktemp)"
|
||||||
|
echo -n "${TEST_CIPHERTEXT01}" > "${CIPHERTEXT_FILE01}"
|
||||||
|
echo -n "${TEST_CIPHERTEXT02}" > "${CIPHERTEXT_FILE02}"
|
||||||
|
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "${CIPHERTEXT_FILE01}" "${CIPHERTEXT_FILE02}"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
export LIBEXIM_PASSWORD="${TEST_PASSWORD}"
|
||||||
|
|
||||||
|
DECRYPTED01="$(src/libexim-encrypt-dlfunc-decrypt-secretbox ${TEST_CIPHERTEXT01})"
|
||||||
|
if [ "${DECRYPTED01}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 1 - decrypt commandline argument with password from environment successful"
|
||||||
|
else
|
||||||
|
echo "not ok 1 - decrypt commandline argument with password from environment unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DECRYPTED02="$(src/libexim-encrypt-dlfunc-decrypt-secretbox --infile ${CIPHERTEXT_FILE01})"
|
||||||
|
if [ "${DECRYPTED02}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 2 - decrypt file contents with password from environment successful"
|
||||||
|
else
|
||||||
|
echo "not ok 2 - decrypt file contents with password from environment unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DECRYPTED03="$(echo -n ${TEST_CIPHERTEXT01} | src/libexim-encrypt-dlfunc-decrypt-secretbox --infile -)"
|
||||||
|
if [ "${DECRYPTED03}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 3 - decrypt stdin contents with password from environment successful"
|
||||||
|
else
|
||||||
|
echo "not ok 3 - decrypt stdin file contents with password from environment unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
export -n LIBEXIM_PASSWORD
|
||||||
|
|
||||||
|
DECRYPTED04="$(src/libexim-encrypt-dlfunc-decrypt-secretbox -p ${TEST_PASSWORD} ${TEST_CIPHERTEXT02})"
|
||||||
|
if [ "${DECRYPTED04}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 4 - decrypt commandline argument with password from commandline successful"
|
||||||
|
else
|
||||||
|
echo "not ok 4 - decrypt commandline argument with password from commandline unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DECRYPTED05="$(src/libexim-encrypt-dlfunc-decrypt-secretbox -p ${TEST_PASSWORD} --infile ${CIPHERTEXT_FILE02})"
|
||||||
|
if [ "${DECRYPTED05}" == "${TEST_CLEARTEXT}" ] ; then
|
||||||
|
echo "ok 5 - decrypt file contents with password from commandline successful"
|
||||||
|
else
|
||||||
|
echo "not ok 5 - decrypt file contents with password from commandline unsuccessful"
|
||||||
|
fi
|
||||||
99
src/test_libexim-encrypt-dlfunc.sh
Executable file
99
src/test_libexim-encrypt-dlfunc.sh
Executable file
@ -0,0 +1,99 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# shellcheck disable=SC2164
|
||||||
|
|
||||||
|
PATH=/sbin:/usr/sbin:$PATH
|
||||||
|
# this script implements the TAP protocol (https://testanything.org)
|
||||||
|
echo 1..6
|
||||||
|
|
||||||
|
# copy to /tmp to keep commandline arguments to exim calls under 256 chars (prevent problems on Ubuntu)
|
||||||
|
install -t /tmp src/libexim-encrypt-dlfunc.so
|
||||||
|
|
||||||
|
LIB=/tmp/libexim-encrypt-dlfunc.so
|
||||||
|
CLEARTEXT="127.88.99.23" # keep short; see above
|
||||||
|
PASSWORD="$(openssl rand -base64 32)"
|
||||||
|
|
||||||
|
CIPHERTEXT=$(exim -C /dev/null -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_encrypt_password}{${PASSWORD}}{${CLEARTEXT}}}")
|
||||||
|
DECRYPTED=$(exim -C /dev/null -be "\${dlfunc{${LIB}}{sodium_crypto_secretbox_decrypt_password}{${PASSWORD}}{${CIPHERTEXT}}}")
|
||||||
|
|
||||||
|
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
|
||||||
|
echo "ok 1 - secretbox test successful"
|
||||||
|
else
|
||||||
|
echo "not ok 1 - secretbox test unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
PK="tgFFIJ9VBnQpcXteqWhgXoEaVGvJgJd4QcYgrmaf2VM="
|
||||||
|
SK="lY1F70Vqwe+uCn4czGdwyGdr0WLUWdkj/Gq39m2k3P0="
|
||||||
|
|
||||||
|
CIPHERTEXT=$(exim -C /dev/null -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal}{${PK}}{${CLEARTEXT}}}")
|
||||||
|
DECRYPTED=$(exim -C /dev/null -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal_open}{${SK}}{${PK}}{${CIPHERTEXT}}}")
|
||||||
|
|
||||||
|
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
|
||||||
|
echo "ok 2 - sealed_box test with pre-generated key pair successful"
|
||||||
|
else
|
||||||
|
echo "not ok 2 - sealed_box test with pre-generated key pair unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# skip test on Ubuntu
|
||||||
|
#[ "$(lsb_release --id --short)" == "Ubuntu" ] && echo "not ok 3 # skip Ubuntu has patches against long commandline arguments, bailing out"
|
||||||
|
|
||||||
|
### Test libexim-encrypt-dlfunc-genkeys
|
||||||
|
|
||||||
|
TEMPDIR01="$(mktemp --directory --quiet)"
|
||||||
|
TEMPDIR02="$(mktemp --directory --quiet)"
|
||||||
|
cleanup() {
|
||||||
|
rm -rf "${TEMPDIR01}" "${TEMPDIR02}"
|
||||||
|
}
|
||||||
|
trap cleanup EXIT INT TERM
|
||||||
|
|
||||||
|
CURDIR="$(pwd)"
|
||||||
|
|
||||||
|
pushd "${TEMPDIR01}" > /dev/null
|
||||||
|
"${CURDIR}/src/libexim-encrypt-dlfunc-genkeys" 2> /dev/null # TAP parser seems to hate the output
|
||||||
|
PK="$(base64 cryptobox_recipient_pk.raw)"
|
||||||
|
SK="$(base64 cryptobox_recipient_sk.raw)"
|
||||||
|
popd > /dev/null
|
||||||
|
|
||||||
|
CIPHERTEXT=$(exim -C /dev/null -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal}{${PK}}{${CLEARTEXT}}}")
|
||||||
|
DECRYPTED=$(exim -C /dev/null -be "\${dlfunc{${LIB}}{sodium_crypto_box_seal_open}{${SK}}{${PK}}{${CIPHERTEXT}}}")
|
||||||
|
|
||||||
|
if [ "${CLEARTEXT}" == "${DECRYPTED}" ] ; then
|
||||||
|
echo "ok 3 - sealed_box test with newly generated key pair successful"
|
||||||
|
else
|
||||||
|
echo "not ok 3 - sealed_box test with newly generated key pair unsuccessful"
|
||||||
|
fi
|
||||||
|
|
||||||
|
### Check if --help works
|
||||||
|
|
||||||
|
if src/libexim-encrypt-dlfunc-decrypt-secretbox --help > /dev/null ; then
|
||||||
|
echo "ok 4 - secretbox --help argument works"
|
||||||
|
else
|
||||||
|
echo "not ok 4 - secretbox --help argument does not work"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if src/libexim-encrypt-dlfunc-decrypt-sealedbox --help > /dev/null ; then
|
||||||
|
echo "ok 5 - sealedbox --help argument works"
|
||||||
|
else
|
||||||
|
echo "not ok 5 - sealedbox --help argument does not work"
|
||||||
|
fi
|
||||||
|
|
||||||
|
### Code coverage for genkeys file access failures
|
||||||
|
pushd "${TEMPDIR02}" > /dev/null
|
||||||
|
KEYFILES=(cryptobox_recipient_pk.raw cryptobox_recipient_pk_exim.conf cryptobox_recipient_sk.raw cryptobox_recipient_sk_exim.conf)
|
||||||
|
for KF in "${KEYFILES[@]}"; do
|
||||||
|
rm -f "${KF}"
|
||||||
|
touch "${KF}"
|
||||||
|
done
|
||||||
|
FS_ACCESS_FAILURE=0
|
||||||
|
for KF in "${KEYFILES[@]}"; do
|
||||||
|
su -s /bin/bash -c "${CURDIR}/src/libexim-encrypt-dlfunc-genkeys" - nobody 2> /dev/null && FS_ACCESS_FAILURE=1
|
||||||
|
rm -f "${KF}"
|
||||||
|
touch "${KF}"
|
||||||
|
chown nobody: "${KF}"
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ${FS_ACCESS_FAILURE} -eq 0 ]; then
|
||||||
|
echo "ok 6 - genkeys should fail without filesystem access"
|
||||||
|
else
|
||||||
|
echo "not ok 6 - genkeys should fail without filesystem access"
|
||||||
|
fi
|
||||||
|
popd > /dev/null
|
||||||
Reference in New Issue
Block a user