mirror of
https://gitlab.kit.edu/kit/scc/sys/mail/exim-encrypt-dlfunc.git
synced 2025-12-06 10:23:55 +01:00
163 lines
5.3 KiB
C
163 lines
5.3 KiB
C
#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);
|
|
} |