From 42f6f25ec0b97dcb35e14125010f2433cfa01452 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 06:22:29 +0000 Subject: [PATCH 1/3] Initial plan From 4a8eae78209147bb15459490486da23b4a637b96 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 06:24:56 +0000 Subject: [PATCH 2/3] fix: implement proper secure_getenv with dlsym fallback Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- .../agent/one-shot-token/one-shot-token.c | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/containers/agent/one-shot-token/one-shot-token.c b/containers/agent/one-shot-token/one-shot-token.c index 73b224fc8..47ae3fe51 100644 --- a/containers/agent/one-shot-token/one-shot-token.c +++ b/containers/agent/one-shot-token/one-shot-token.c @@ -45,6 +45,12 @@ static pthread_mutex_t token_mutex = PTHREAD_MUTEX_INITIALIZER; /* Pointer to the real getenv function */ static char *(*real_getenv)(const char *name) = NULL; +/* Pointer to the real secure_getenv function (may be NULL if unavailable) */ +static char *(*real_secure_getenv)(const char *name) = NULL; + +/* Flag to track whether we've tried to resolve secure_getenv */ +static int secure_getenv_initialized = 0; + /* Initialize the real getenv pointer */ static void init_real_getenv(void) { if (real_getenv == NULL) { @@ -57,6 +63,18 @@ static void init_real_getenv(void) { } } +/* Initialize the real secure_getenv pointer */ +static void init_real_secure_getenv(void) { + if (!secure_getenv_initialized) { + real_secure_getenv = dlsym(RTLD_NEXT, "secure_getenv"); + if (real_secure_getenv == NULL) { + /* secure_getenv is not available on all systems, which is fine */ + fprintf(stderr, "[one-shot-token] INFO: secure_getenv not available, will fall back to getenv\n"); + } + secure_getenv_initialized = 1; + } +} + /* Check if a variable name is a sensitive token */ static int get_token_index(const char *name) { if (name == NULL) return -1; @@ -122,11 +140,62 @@ char *getenv(const char *name) { } /** - * Also intercept secure_getenv for completeness - * (some security-conscious code uses this instead of getenv) + * Intercepted secure_getenv function + * + * This function preserves secure_getenv semantics (returns NULL in privileged contexts) + * while applying the same one-shot token protection as getenv. + * + * For sensitive tokens: + * - First call: returns the real value (if not in privileged context), then unsets the variable + * - Subsequent calls: returns NULL + * + * For all other variables: passes through to real secure_getenv (or getenv if unavailable) */ char *secure_getenv(const char *name) { - /* secure_getenv returns NULL if the program is running with elevated privileges. - * We delegate to our intercepted getenv which handles the one-shot logic. */ - return getenv(name); + init_real_secure_getenv(); + init_real_getenv(); + + /* If secure_getenv is not available, fall back to our intercepted getenv */ + if (real_secure_getenv == NULL) { + return getenv(name); + } + + int token_idx = get_token_index(name); + + /* Not a sensitive token - pass through to real secure_getenv */ + if (token_idx < 0) { + return real_secure_getenv(name); + } + + /* Sensitive token - handle one-shot access with secure_getenv semantics */ + pthread_mutex_lock(&token_mutex); + + char *result = NULL; + + if (!token_accessed[token_idx]) { + /* First access - get the real value using secure_getenv */ + result = real_secure_getenv(name); + + if (result != NULL) { + /* Make a copy since unsetenv will invalidate the pointer */ + /* Note: This memory is intentionally never freed - it must persist + * for the lifetime of the caller's use of the returned pointer */ + result = strdup(result); + + /* Unset the variable so it can't be accessed again */ + unsetenv(name); + + fprintf(stderr, "[one-shot-token] Token %s accessed and cleared (via secure_getenv)\n", name); + } + + /* Mark as accessed even if NULL (prevents repeated log messages) */ + token_accessed[token_idx] = 1; + } else { + /* Already accessed - return NULL */ + result = NULL; + } + + pthread_mutex_unlock(&token_mutex); + + return result; } From 10fd9bd77e93324afd77d7a854ace12250e2da30 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 10 Feb 2026 06:26:48 +0000 Subject: [PATCH 3/3] fix: add thread safety to secure_getenv initialization Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com> --- containers/agent/one-shot-token/one-shot-token.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/containers/agent/one-shot-token/one-shot-token.c b/containers/agent/one-shot-token/one-shot-token.c index 47ae3fe51..d3e921dbe 100644 --- a/containers/agent/one-shot-token/one-shot-token.c +++ b/containers/agent/one-shot-token/one-shot-token.c @@ -63,16 +63,19 @@ static void init_real_getenv(void) { } } -/* Initialize the real secure_getenv pointer */ +/* Initialize the real secure_getenv pointer (thread-safe) */ static void init_real_secure_getenv(void) { + pthread_mutex_lock(&token_mutex); if (!secure_getenv_initialized) { real_secure_getenv = dlsym(RTLD_NEXT, "secure_getenv"); + secure_getenv_initialized = 1; + /* Only log once when secure_getenv is not available */ if (real_secure_getenv == NULL) { /* secure_getenv is not available on all systems, which is fine */ fprintf(stderr, "[one-shot-token] INFO: secure_getenv not available, will fall back to getenv\n"); } - secure_getenv_initialized = 1; } + pthread_mutex_unlock(&token_mutex); } /* Check if a variable name is a sensitive token */