/******************************************************************************/ /* SlunkCrypt, by LoRd_MuldeR */ /* This work has been released under the CC0 1.0 Universal license! */ /******************************************************************************/ #ifdef _WIN32 # define _CRT_SECURE_NO_WARNINGS 1 #else # define _GNU_SOURCE 1 #endif /* Internal */ #include #include "utils.h" #include "crypt.h" #include "pwgen.h" #include "selftest.h" /* CRT */ #include #include #include #include #include // ========================================================================== // Constants // ========================================================================== #define MODE_HELP 0 #define MODE_VERS 1 #define MODE_ENCR 2 #define MODE_DECR 3 #define MODE_PASS 4 #define MODE_TEST 5 static const size_t RCMD_PWDLEN_LENGTH = 12U; static const size_t DFLT_PWDLEN_LENGTH = 24U; static const CHR* const ENV_PASSWORD = T("SLUNK_PASSPHRASE"); static const CHR* const ENV_KEEPFILE = T("SLUNK_KEEP_INCOMPLETE"); static const CHR* const PREFIX_PASS = T("pass:"); static const CHR* const PREFIX_FILE = T("file:"); // ========================================================================== // Auxiliary functions // ========================================================================== #define PW_FROM_ENV (!(argc > 4)) static int parse_slunk_mode(const CHR* const command) { if ((!STRICMP(command, T("-h"))) || (!STRICMP(command, T("/?"))) || (!STRICMP(command, T("--help")))) { return MODE_HELP; } else if ((!STRICMP(command, T("-v"))) || (!STRICMP(command, T("--version")))) { return MODE_VERS; } else if ((!STRICMP(command, T("-e"))) || (!STRICMP(command, T("--encrypt")))) { return MODE_ENCR; } else if ((!STRICMP(command, T("-d"))) || (!STRICMP(command, T("--decrypt")))) { return MODE_DECR; } else if ((!STRICMP(command, T("-p"))) || (!STRICMP(command, T("--make-pw")))) { return MODE_PASS; } else if ((!STRICMP(command, T("-t"))) || (!STRICMP(command, T("--self-test")))) { return MODE_TEST; } else { return -1; /*invalid command*/ } } static void print_manpage(const CHR *const program) { FPUTS(T("====================================================================\n"), stderr); FPUTS(T("This software has been released under the CC0 1.0 Universal license:\n"), stderr); FPUTS(T("https://creativecommons.org/publicdomain/zero/1.0/legalcode\n"), stderr); FPUTS(T("====================================================================\n\n"), stderr); FPUTS(T("Usage:\n"), stderr); FPRINTF(stderr, T(" %") T(PRISTR) T(" --encrypt [pass:|file:] \n"), program); FPRINTF(stderr, T(" %") T(PRISTR) T(" --decrypt [pass:|file:] \n"), program); FPRINTF(stderr, T(" %") T(PRISTR) T(" --make-pw []\n\n"), program); FPRINTF(stderr, T("Optionally, reads passphrase from the %") T(PRISTR) T(" environment variable.\n\n"), ENV_PASSWORD); } static char *copy_passphrase(const CHR *const passphrase) { if ((!passphrase) || (!passphrase[0U])) { FPUTS(T("Error: The passphrase input string must not be empty!\n\n"), stderr); return NULL; } char *const buffer = CHR_to_utf8(passphrase); if (!buffer) { FPUTS(T("Error: Failed to allocate the string buffer!\n\n"), stderr); } return buffer; } static int keep_incomplete_files(void) { const CHR *const keep_files = GETENV(ENV_KEEPFILE); if (keep_files) { return BOOLIFY(STRTOUL(keep_files)); } return 0; } static void check_excess_arguments(const int argc, int maximum) { if (argc > maximum) { FPUTS(T("Warning: Excess command-line argument(s) will be ignored!\n\n"), stderr); fflush(stderr); } } static void sigint_handler(const int sig) { if (sig == SIGINT) { g_slunkcrypt_abort_flag = 1; } } // ========================================================================== // Main function // ========================================================================== int MAIN(const int argc, CHR *const argv[]) { init_terminal(); setup_signal_handler(SIGINT, sigint_handler); int result = EXIT_FAILURE; char *passphrase_buffer = NULL; FPRINTF(stderr, T("SlunkCrypt Utility (%") T(PRIstr) T("-%") T(PRIstr) T("), by LoRd_MuldeR \n"), OS_TYPE, CPU_ARCH); FPRINTF(stderr, T("Using libSlunkCrypt v%u.%u.%u [%") T(PRIstr) T("]\n\n"), SLUNKCRYPT_VERSION_MAJOR, SLUNKCRYPT_VERSION_MINOR, SLUNKCRYPT_VERSION_PATCH, SLUNKCRYPT_BUILD); fflush(stderr); /* ----------------------------------------------------- */ /* Parse arguments */ /* ----------------------------------------------------- */ if (argc < 1) { FPUTS(T("Error: Argument array is empty. The program was called incorrectly!\n\n"), stderr); goto clean_up; } if (argc < 2) { FPRINTF(stderr, T("Error: Nothing to do. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U])); goto clean_up; } const int slunk_mode = parse_slunk_mode(argv[1U]); switch (slunk_mode) { case MODE_HELP: print_manpage(get_file_name(argv[0U])); case MODE_VERS: result = EXIT_SUCCESS; goto clean_up; case MODE_ENCR: case MODE_DECR: break; /*fallthrough*/ case MODE_PASS: check_excess_arguments(argc, 3); result = generate_passphrase((argc > 2) ? STRTOUL(argv[2U]) : DFLT_PWDLEN_LENGTH); goto clean_up; case MODE_TEST: check_excess_arguments(argc, 2); result = run_selftest_routine(); goto clean_up; default: FPRINTF(stderr, T("Error: The specified command \"%") T(PRISTR) T("\" is unknown!\n\n"), argv[1U]); goto clean_up; } if (argc < 4) { FPRINTF(stderr, T("Error: Required argument is missing. Please type '%") T(PRISTR) T(" --help' for details!\n\n"), get_file_name(argv[0U])); goto clean_up; } check_excess_arguments(argc, 5); const CHR *const passphrase = PW_FROM_ENV ? GETENV(ENV_PASSWORD) : argv[2U]; const CHR *const input_file = argv[PW_FROM_ENV ? 2U : 3U], *const output_file = argv[PW_FROM_ENV ? 3U : 4U]; if ((!passphrase) || (!passphrase[0U])) { FPUTS(T("Error: The passphrase must be specified, directly or indirectly!\n\n"), stderr); goto clean_up; } if ((!PW_FROM_ENV) && STRICMP(passphrase, T("-"))) { if ((!STARTS_WITH(passphrase, PREFIX_PASS)) && (!STARTS_WITH(passphrase, PREFIX_FILE))) { FPRINTF(stderr, T("Error: The passphrase must start with a '%") T(PRISTR) T("' or '%") T(PRISTR) T("' prefix!\n\n"), PREFIX_PASS, PREFIX_FILE); goto clean_up; } } if ((!input_file[0U]) || (!output_file[0U])) { FPUTS(T("Error: The input file and/or output file must not be empty!\n\n"), stderr); goto clean_up; } /* ----------------------------------------------------- */ /* Initialize passphrase */ /* ----------------------------------------------------- */ if (!(passphrase_buffer = PW_FROM_ENV ? copy_passphrase(passphrase) : (STARTS_WITH(passphrase, PREFIX_PASS) ? copy_passphrase(passphrase + STRLEN(PREFIX_PASS)) : (STARTS_WITH(passphrase, PREFIX_FILE) ? read_passphrase(passphrase + STRLEN(PREFIX_FILE)) : read_passphrase(T("-")))))) { goto clean_up; } slunkcrypt_bzero((CHR*)passphrase, STRLEN(passphrase) * sizeof(CHR)); const size_t passphrase_len = strlen(passphrase_buffer); if (passphrase_len < SLUNKCRYPT_PWDLEN_MIN) { FPRINTF(stderr, T("Error: Passphrase must be at least %u characters in length!\n\n"), (unsigned)SLUNKCRYPT_PWDLEN_MIN); goto clean_up; } else if (passphrase_len > SLUNKCRYPT_PWDLEN_MAX) { FPRINTF(stderr, T("Error: Passphrase must be at most %u characters in length!\n\n"), (unsigned)SLUNKCRYPT_PWDLEN_MAX); goto clean_up; } if (slunk_mode == MODE_ENCR) { if (passphrase_len < RCMD_PWDLEN_LENGTH) { FPRINTF(stderr, T("Warning: Using a *short* passphrase; a length of %u characters or more is recommended!\n\n"), (unsigned)RCMD_PWDLEN_LENGTH); } else if (weak_passphrase(passphrase_buffer)) { FPUTS(T("Warning: Using a *weak* passphrase; a mix of upper-case letters, lower-case letters, digits and other characters is recommended!\n\n"), stderr); } } fflush(stderr); /* ----------------------------------------------------- */ /* Encrypt or decrypt */ /* ----------------------------------------------------- */ const clock_t clk_start = clock(); const int keep_incomplete = keep_incomplete_files(); switch (slunk_mode) { case MODE_ENCR: result = encrypt(passphrase_buffer, input_file, output_file, keep_incomplete); break; case MODE_DECR: result = decrypt(passphrase_buffer, input_file, output_file, keep_incomplete); break; default: FPUTS(T("Unexpected mode encountered!\n\n"), stderr); } if (!g_slunkcrypt_abort_flag) { const clock_t clk_end = clock(); FPRINTF(stderr, T("--------\n\nOperation completed after %.1f seconds.\n\n"), (clk_end - clk_start) / ((double)CLOCKS_PER_SEC)); } /* ----------------------------------------------------- */ /* Final clean-up */ /* ----------------------------------------------------- */ clean_up: fflush(stderr); if (passphrase_buffer) { slunkcrypt_bzero(passphrase_buffer, strlen(passphrase_buffer)); free(passphrase_buffer); } return result; } #if defined(_WIN32) && defined(__MINGW32__) && !defined(__MINGW64_VERSION_MAJOR) void __wgetmainargs(int*, wchar_t***, wchar_t***, int, int*); int main() { wchar_t** enpv, ** argv; int argc, si = 0; __wgetmainargs(&argc, &argv, &enpv, 1, &si); return wmain(argc, argv); } #endif