chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

View File

@@ -0,0 +1,344 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <errno.h>
#include <openssl/buf.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
#include "internal.h"
#include "../internal.h"
// We support two types of terminal interface:
// - termios for Linux/Unix
// - WIN32 Console for Windows
#if !defined(OPENSSL_WINDOWS)
#include <termios.h>
#define DEV_TTY "/dev/tty"
#define TTY_STRUCT struct termios
#define TTY_FLAGS c_lflag
#define TTY_get(tty,data) tcgetattr(tty, data)
#define TTY_set(tty,data) tcsetattr(tty, TCSANOW, data)
#else /* OPENSSL_WINDOWS */
#include <windows.h>
#endif
#define NUM_SIG 32
static volatile sig_atomic_t intr_signal;
static struct CRYPTO_STATIC_MUTEX console_global_mutex = CRYPTO_STATIC_MUTEX_INIT;
#if !defined(OPENSSL_WINDOWS)
static struct sigaction savsig[NUM_SIG];
#else
static void (*savsig[NUM_SIG]) (int);
#endif
#if defined(OPENSSL_WINDOWS)
DWORD tty_orig, tty_new;
#else
TTY_STRUCT tty_orig, tty_new;
#endif
FILE *tty_in, *tty_out;
int is_a_tty;
static void popsig(void) {
#if !defined(OPENSSL_WINDOWS)
for (int i = 1; i < NUM_SIG; i++) {
if (i == SIGUSR1 || i == SIGUSR2 || i == SIGKILL) {
continue;
}
sigaction(i, &savsig[i], NULL);
}
#else
signal(SIGABRT, savsig[SIGABRT]);
signal(SIGFPE, savsig[SIGFPE]);
signal(SIGILL, savsig[SIGILL]);
signal(SIGINT, savsig[SIGINT]);
signal(SIGSEGV, savsig[SIGSEGV]);
signal(SIGTERM, savsig[SIGTERM]);
#endif
}
static void recsig(int signal) {
intr_signal = signal;
}
static int discard_line_remainder(FILE *in) {
char buf[5];
do {
if (!fgets(buf, 4, in)) {
if (ferror(in)) {
OPENSSL_PUT_ERROR(PEM, PEM_R_PROBLEMS_GETTING_PASSWORD);
ERR_add_error_data(2, "System error: ", strerror(errno));
clearerr(tty_in);
} else if(feof(in)) {
return 1;
}
return 0;
}
} while (strchr(buf, '\n') == NULL);
return 1;
}
/* Signal handling functions */
static void pushsig(void) {
#if !defined(OPENSSL_WINDOWS)
struct sigaction sa;
OPENSSL_cleanse(&sa, sizeof(sa));
sa.sa_handler = recsig;
for (int i = 1; i < NUM_SIG; i++) {
if (i == SIGUSR1 || i == SIGUSR2 || i == SIGKILL) {
continue;
}
sigaction(i, &sa, &savsig[i]);
}
#else // Specific error codes for windows
savsig[SIGABRT] = signal(SIGABRT, recsig);
savsig[SIGFPE] = signal(SIGFPE, recsig);
savsig[SIGILL] = signal(SIGILL, recsig);
savsig[SIGINT] = signal(SIGINT, recsig);
savsig[SIGSEGV] = signal(SIGSEGV, recsig);
savsig[SIGTERM] = signal(SIGTERM, recsig);
#endif
// set SIGWINCH handler to default so our workflow is not
// triggered on user resizing of window
#if defined(SIGWINCH) && defined(SIG_DFL)
signal(SIGWINCH, SIG_DFL);
#endif
}
/* Console management functions */
void openssl_console_acquire_mutex(void) {
CRYPTO_STATIC_MUTEX_lock_write(&console_global_mutex);
}
void openssl_console_release_mutex(void) {
CRYPTO_STATIC_MUTEX_unlock_write(&console_global_mutex);
}
int openssl_console_open(void) {
is_a_tty = 1;
assert(CRYPTO_STATIC_MUTEX_is_write_locked(&console_global_mutex));
// Check for test environment variable first (platform-independent)
const char* test_mode = getenv("AWSLC_CONSOLE_NO_TTY_DETECT");
if (test_mode != NULL) {
tty_in = stdin;
tty_out = stderr;
is_a_tty = 0;
return 1;
}
#if !defined(OPENSSL_WINDOWS)
if ((tty_in = fopen(DEV_TTY, "r")) == NULL) {
tty_in = stdin;
}
if ((tty_out = fopen(DEV_TTY, "w")) == NULL) {
tty_out = stderr;
}
if (TTY_get(fileno(tty_in), &tty_orig) == -1) {
if (errno == ENOTTY || errno == EINVAL || errno == ENXIO || errno == EIO
|| errno == EPERM || errno == ENODEV) {
is_a_tty = 0;
} else {
OPENSSL_PUT_ERROR(PEM, ERR_R_INTERNAL_ERROR);
return 0;
}
}
#else
DWORD console_mode;
HANDLE hStdIn = GetStdHandle(STD_INPUT_HANDLE);
// Check if console (equivalent to checking for /dev/tty on Linux)
if (GetConsoleMode(hStdIn, &console_mode)) {
// It's a real console, use conin$ and conout$ to bypass any redirection
if ((tty_in = fopen("conin$", "r")) == NULL) {
tty_in = stdin;
}
if ((tty_out = fopen("conout$", "w")) == NULL) {
tty_out = stderr;
}
tty_orig = console_mode;
} else {
// Not a console, use stdin/stderr
tty_in = stdin;
tty_out = stderr;
is_a_tty = 0;
}
#endif
return 1;
}
int openssl_console_close(void) {
assert(CRYPTO_STATIC_MUTEX_is_write_locked(&console_global_mutex));
if (tty_in != stdin) {
fclose(tty_in);
}
if (tty_out != stderr) {
fclose(tty_out);
}
return 1;
}
static int openssl_console_echo_disable(void) {
#if !defined(OPENSSL_WINDOWS)
OPENSSL_memcpy(&(tty_new), &(tty_orig), sizeof(tty_orig));
tty_new.TTY_FLAGS &= ~ECHO;
if (is_a_tty && (TTY_set(fileno(tty_in), &tty_new) == -1)) {
OPENSSL_PUT_ERROR(PEM, ERR_R_INTERNAL_ERROR);
return 0;
}
#else
if (is_a_tty) {
tty_new = tty_orig;
tty_new &= ~ENABLE_ECHO_INPUT;
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), tty_new);
}
#endif
return 1;
}
static int openssl_console_echo_enable(void) {
#if !defined(OPENSSL_WINDOWS)
OPENSSL_memcpy(&(tty_new), &(tty_orig), sizeof(tty_orig));
if (is_a_tty && (TTY_set(fileno(tty_in), &tty_new) == -1)) {
OPENSSL_PUT_ERROR(PEM, ERR_R_INTERNAL_ERROR);
return 0;
}
#else
if (is_a_tty) {
tty_new = tty_orig;
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), tty_new);
}
#endif
return 1;
}
int openssl_console_write(const char *str) {
assert(CRYPTO_STATIC_MUTEX_is_write_locked(&console_global_mutex));
if (fputs(str, tty_out) < 0 || fflush(tty_out) != 0) {
OPENSSL_PUT_ERROR(PEM, PEM_R_PROBLEMS_GETTING_PASSWORD);
if (ferror(tty_out)) {
ERR_add_error_data(2, "System error: ", strerror(errno));
clearerr(tty_out);
}
return 0;
}
return 1;
}
// returns 0 on success, -1 on error, -2 if interrupt signal received
int openssl_console_read(char *buf, int minsize, int maxsize, int echo) {
int ok = 0;
char *p = NULL;
int echo_eol = !echo;
intr_signal = 0;
int phase = 0;
if (!buf || maxsize < minsize) {
return -1;
}
assert(CRYPTO_STATIC_MUTEX_is_write_locked(&console_global_mutex));
pushsig();
phase = 1;
if (!echo && !openssl_console_echo_disable()) {
goto error;
}
phase = 2;
buf[0] = '\0';
#if defined(OPENSSL_WINDOWS)
if (is_a_tty) {
DWORD numread;
// for now assuming UTF-8....
WCHAR wresult[BUFSIZ];
OPENSSL_cleanse(wresult, sizeof(wresult));
if (ReadConsoleW(GetStdHandle(STD_INPUT_HANDLE),
wresult, maxsize, &numread, NULL)) {
if (numread >= 2 && wresult[numread-2] == L'\r' &&
wresult[numread-1] == L'\n') {
wresult[numread-2] = L'\n';
numread--;
}
wresult[numread] = L'\0';
if (WideCharToMultiByte(CP_UTF8, 0, wresult, -1, buf, maxsize + 1, NULL, 0) > 0) {
p = buf;
}
OPENSSL_cleanse(wresult, sizeof(wresult));
}
} else {
p = fgets(buf, maxsize, tty_in);
}
#else
p = fgets(buf, maxsize, tty_in);
#endif
if (p == NULL || feof(tty_in) || ferror(tty_in)) {
OPENSSL_PUT_ERROR(PEM, PEM_R_PROBLEMS_GETTING_PASSWORD);
if (ferror(tty_in)) {
ERR_add_error_data(2, "System error: ", strerror(errno));
clearerr(tty_in);
}
ok = -1;
goto error;
}
// check if we see a new line, otherwise clear out remaining input buffer
if ((p = strchr(buf, '\n')) != NULL) {
*p = '\0';
} else if (!discard_line_remainder(tty_in)) {
ok = -1;
goto error;
}
// Validate that input meets the minimum length requirement. We accept buffers |buf| of any size
// but enforce a maximum password length of 1024 characters for compatibility with OpenSSL.
// Unlike OpenSSL's higher-level APIs which silently truncate buffers exceeding this limit,
// we explicitly check the password length after reading it from the user. This approach maintains
// OpenSSL compatibility while avoiding silent truncation.
size_t input_len = strlen(buf);
if (input_len < (size_t)minsize || input_len > (size_t)MAX_PASSWORD_LENGTH) {
ok = -1;
}
error:
if (intr_signal == SIGINT) {
ok = -2; // interrupted
}
if (echo_eol) {
fprintf(tty_out, "\n");
}
if (phase >= 2 && !echo && !openssl_console_echo_enable()) {
ok = -1; // general errors
}
if (phase >= 1) {
popsig();
}
return ok;
}

View File

@@ -0,0 +1,252 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <gtest/gtest.h>
#include <signal.h>
#include "internal.h"
#include "../test/test_util.h"
#if defined(OPENSSL_WINDOWS)
#include <io.h>
#define dup _dup
#define dup2 _dup2
#define fileno _fileno
#define close _close
#else
#include <unistd.h>
#endif
#if !defined(OPENSSL_ANDROID)
// On Android, when running from an APK, |tmpfile| does not work. See
// b/36991167#comment8.
// Consolidated password testing
class PemPasswdTest : public testing::Test {
protected:
void SetUp() override {
#if defined(OPENSSL_WINDOWS)
_putenv_s("AWSLC_CONSOLE_NO_TTY_DETECT", "1");
#else
setenv("AWSLC_CONSOLE_NO_TTY_DETECT", "1", 1);
#endif
// Save original file descriptors
original_stdin = dup(fileno(stdin));
original_stderr = dup(fileno(stderr));
// Create temporary files
stdin_file = createRawTempFILE();
stderr_file = createRawTempFILE();
ASSERT_TRUE(stdin_file != nullptr);
ASSERT_TRUE(stderr_file != nullptr);
// Redirect stdin/stderr to our temp files
ASSERT_NE(-1, dup2(fileno(stdin_file), fileno(stdin)));
ASSERT_NE(-1, dup2(fileno(stderr_file), fileno(stderr)));
// Initialize console for each test
openssl_console_acquire_mutex();
ASSERT_TRUE(openssl_console_open());
}
void TearDown() override {
#if defined(OPENSSL_WINDOWS)
_putenv_s("AWSLC_CONSOLE_NO_TTY_DETECT", "");
#else
unsetenv("AWSLC_CONSOLE_NO_TTY_DETECT");
#endif
// Close console for each test
ASSERT_TRUE(openssl_console_close());
openssl_console_release_mutex();
// Restore original streams
ASSERT_NE(-1, dup2(original_stdin, fileno(stdin)));
ASSERT_NE(-1, dup2(original_stderr, fileno(stderr)));
// Close temp files
if (stdin_file) {
fclose(stdin_file);
}
if (stderr_file) {
fclose(stderr_file);
}
}
void MockStdinInput(const std::string& input) {
ASSERT_GT(fwrite(input.c_str(), 1, input.length(), stdin_file), (size_t)0);
rewind(stdin_file);
}
std::string GetStderrOutput() {
std::string output;
char buf[1024];
rewind(stderr_file);
while (fgets(buf, sizeof(buf), stderr_file) != nullptr) {
output += buf;
}
return output;
}
void ResetTempFiles() {
fclose(stdin_file);
fclose(stderr_file);
stdin_file = tmpfile();
stderr_file = tmpfile();
ASSERT_TRUE(stdin_file != nullptr);
ASSERT_TRUE(stderr_file != nullptr);
// Redirect stdin/stderr to our NEW temp files
ASSERT_NE(-1, dup2(fileno(stdin_file), fileno(stdin)));
ASSERT_NE(-1, dup2(fileno(stderr_file), fileno(stderr)));
}
FILE* stdin_file = nullptr;
FILE* stderr_file = nullptr;
int original_stdin = -1;
int original_stderr = -1;
const char* default_prompt = "Enter password:";
};
// Test basic password functionality with various inputs
TEST_F(PemPasswdTest, PasswordInputVariations) {
struct TestCase {
std::string description;
std::string input;
int min_size;
int expected_result;
std::string expected_output;
};
std::vector<TestCase> test_cases = {
// Normal password
{"Normal password", "test_password\n", 0, 0, "test_password"},
//
// // Empty password
{"Empty password allowed", "\n", 0, 0, ""},
{"Empty password rejected", "\n", 2, -1, ""},
// Length requirements
{"Password too short", "short\n", 10, -1, "short"},
{"Password meets min length", "longenoughpass\n", 10, 0, "longenoughpass"},
// Special characters
{"Special characters", "!@#$%^&*()\n", 0, 0, "!@#$%^&*()"},
{"Unicode characters", "パスワード\n", 0, 0, "パスワード"}
};
for (const auto& tc : test_cases) {
SCOPED_TRACE(tc.description);
char buf[1024] = {0};
MockStdinInput(tc.input);
ASSERT_TRUE(openssl_console_write(default_prompt));
ASSERT_EQ(openssl_console_read(buf, tc.min_size, sizeof(buf), 0), tc.expected_result);
if (tc.expected_result == 0) {
ASSERT_STREQ(buf, tc.expected_output.c_str());
}
// Verify prompt was written
std::string output = GetStderrOutput();
ASSERT_TRUE(output.find(default_prompt) != std::string::npos);
ResetTempFiles();
}
}
// Test password verification flow (matching and non-matching)
TEST_F(PemPasswdTest, PasswordVerification) {
struct TestCase {
std::string description;
std::string first_password;
std::string second_password;
bool should_match;
};
std::vector<TestCase> test_cases = {
{"Matching passwords", "test_password\n", "test_password\n", true},
{"Non-matching passwords", "password1\n", "password2\n", false}
};
for (const auto& tc : test_cases) {
SCOPED_TRACE(tc.description);
char buf1[1024] = {0};
char buf2[1024] = {0};
// Mock both password inputs
std::string combined_input = tc.first_password + tc.second_password;
MockStdinInput(combined_input);
// First password entry
ASSERT_TRUE(openssl_console_write(default_prompt));
ASSERT_EQ(0, openssl_console_read(buf1, 0, sizeof(buf1), 0));
// Verification prompt
ASSERT_TRUE(openssl_console_write("Verifying - "));
ASSERT_TRUE(openssl_console_write(default_prompt));
ASSERT_EQ(0, openssl_console_read(buf2, 0, sizeof(buf2), 0));
// Verify match/mismatch as expected
if (tc.should_match) {
ASSERT_STREQ(buf1, buf2);
} else {
ASSERT_STRNE(buf1, buf2);
}
// Verify prompts were written
std::string output = GetStderrOutput();
ASSERT_TRUE(output.find(default_prompt) != std::string::npos);
ASSERT_TRUE(output.find("Verifying - ") != std::string::npos);
ResetTempFiles();
}
}
// Test buffer handling (truncation of long passwords)
TEST_F(PemPasswdTest, BufferHandling) {
// Small buffer to test truncation
char small_buf[16] = {0};
// Create a password longer than the buffer
std::string long_password(32, 'a');
long_password += "\n";
MockStdinInput(long_password);
ASSERT_TRUE(openssl_console_write(default_prompt));
ASSERT_EQ(0, openssl_console_read(small_buf, 0, sizeof(small_buf),0));
// Verify the password was truncated to fit the buffer (15 chars + null terminator)
std::string expected(15, 'a');
ASSERT_STREQ(small_buf, expected.c_str());
}
// Test echo modes
TEST_F(PemPasswdTest, EchoModes) {
const char* test_password = "test_password\n";
char buf_no_echo[1024] = {0};
char buf_with_echo[1024] = {0};
// Test with echo disabled
MockStdinInput(test_password);
ASSERT_TRUE(openssl_console_write(default_prompt));
ASSERT_EQ(0, openssl_console_read(buf_no_echo, 0, sizeof(buf_no_echo), 0));
// Test with echo enabled
MockStdinInput(test_password);
ASSERT_TRUE(openssl_console_write(default_prompt));
ASSERT_EQ(0, openssl_console_read(buf_with_echo, 0, sizeof(buf_with_echo), 1));
// Both should have the same result
ASSERT_STREQ(buf_no_echo, "test_password");
ASSERT_STREQ(buf_with_echo, "test_password");
}
#endif

View File

@@ -0,0 +1,37 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
#include <openssl/base.h>
#define MIN_LENGTH 4
#define MAX_PASSWORD_LENGTH 1024
#if defined(__cplusplus)
extern "C" {
#endif
// Console Management API
//
// This API provides functions for secure console I/O operations, supporting both Unix/Linux
// (termios) and Windows systems. It handles terminal operations like reading input with
// optional echo suppression (for password entry) and signal handling.
//
// Usage requires proper lock management:
// 1. Acquire the console mutex with openssl_console_acquire_mutex()
// 2. Initialize console with openssl_console_open()
// 3. Perform console operations (read/write)
// 4. Clean up with openssl_console_close()
// 5. Release the mutex with openssl_console_release_mutex()
//
// The global mutex must be held during all console operations and released after closing the console.
OPENSSL_EXPORT void openssl_console_acquire_mutex(void);
OPENSSL_EXPORT void openssl_console_release_mutex(void);
OPENSSL_EXPORT int openssl_console_open(void);
OPENSSL_EXPORT int openssl_console_close(void);
OPENSSL_EXPORT int openssl_console_write(const char *str);
OPENSSL_EXPORT int openssl_console_read(char *buf, int minsize, int maxsize, int echo);
#if defined(__cplusplus)
} // extern C
#endif