C99 defines getenv()
to have the following behavior: [[ISO/IEC 9899:1999]]
The getenv function returns a pointer to a string associated with the matched list member. The string pointed to shall not be modified by the program, but may be overwritten by a subsequent call to the getenv function.
Consequently, it is best not to store this pointer as it may be overwritten by a subsequent call to the getenv()
function or invalidated as a result of changes made to the environment list through calls to putenv()
, setenv()
, or other means. Storing the pointer for later use can result in a dangling pointer or a pointer to incorrect data. This string should be referenced immediately and discarded, or copied so that the copy may be referenced safely at a later time.
The getenv()
function is not thread-safe. Make sure to address any possible race conditions resulting from the use of this function.
Non-Compliant Code Example
This non-compliant code example compares the value of the TMP
and TEMP
environment variables to determine if they are the same. This code example is non-compliant because the string referenced by tmpvar
may be overwritten as a result of the second call to the getenv()
function. As a result, it is possible that both tmpvar
and tempvar
will compare equal even if the two environment variables have different values.
char *tmpvar; char *tempvar; tmpvar = getenv("TMP"); if (!tmpvar) return -1; tempvar = getenv("TEMP"); if (!tempvar) return -1; if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } }
Compliant Solution (Windows)
Windows provides the getenv_s()
and _wgetenv_s()
functions for getting a value from the current environment [[MSDN]].
char *tmpvar; char *tempvar; size_t requiredSize; getenv_s(&requiredSize, NULL, 0, "TMP"); tmpvar = (char *)malloc(requiredSize * sizeof(char)); if (!tmpvar) { /* Handle error */ } getenv_s(&requiredSize, tmpvar, requiredSize, "TMP" ); getenv_s(&requiredSize, NULL, 0, "TEMP"); tempvar = (char *)malloc(requiredSize * sizeof(char)); if (!tempvar) { free(tmpvar); tmpvar = NULL; /* Handle error */ } getenv_s(&requiredSize, tempvar, requiredSize, "TEMP" ); if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Compliant Solution (Windows)
Windows also provides the _dupenv_s()
and _wdupenv_s()
functions for getting a value from the current environment [[MSDN]].
The _dupenv_s()
function searches the list of environment variables for a specified name. If the name is found, a buffer is allocated, the variable's value is copied into the buffer, and the buffer's address and number of elements are returned. By allocating the buffer itself, _dupenv_s()
and _wdupenv_s()
provide a more convenient alternative to getenv_s()
and _wgetenv_s()
.
It is the calling program's responsibility to free any allocated buffers returned by these functions.
char *tmpvar; char *tempvar; size_t len; errno_t err = _dupenv_s(&tmpvar, &len, "TMP"); if (err) return -1; errno_t err = _dupenv_s(&tempvar, &len, "TEMP"); if (err) { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Compliant Solution (POSIX)
POSIX provides the strdup()
function which can make a copy of the environment variable string [[Open Group 04]]. The strdup()
function is also included in ISO/IEC PDTR 24731-2 [[ISO/IEC PDTR 24731-2]].
char *tmpvar; char *tempvar; char *temp = getenv("TMP"); if (temp != NULL) { tmpvar = strdup(temp); if (tmpvar == NULL) { /* Handle Error */ } } else { return -1; } temp = getenv("TEMP"); if (temp != NULL) { tempvar = strdup(temp); if (tempvar == NULL) { free(tmpvar); tmpvar = NULL; /* Handle Error */ } } else { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Compliant Solution
This compliant solution uses only the C99 malloc()
and strcpy()
functions to copy the string returned by getenv()
into a dynamically allocated buffer.
char *tmpvar; char *tempvar; char *temp = getenv("TMP"); if (temp != NULL) { tmpvar = (char *)malloc(strlen(temp)+1); if (tmpvar != NULL) { strcpy(tmpvar, temp); } else { /* Handle Error */ } } else { return -1; } temp = getenv("TEMP"); if (temp != NULL) { tempvar = (char *)malloc(strlen(temp)+1); if (tempvar != NULL) { strcpy(tempvar, temp); } else { free(tmpvar); tmpvar = NULL; /* Handle Error */ } } else { free(tmpvar); tmpvar = NULL; return -1; } if (strcmp(tmpvar, tempvar) == 0) { if (puts("TMP and TEMP are the same.\n") == EOF) { /* Handle Error */ } } else { if (puts("TMP and TEMP are NOT the same.\n") == EOF) { /* Handle Error */ } } free(tmpvar); tmpvar = NULL; free(tempvar); tempvar = NULL;
Risk Assessment
Storing the pointer to the string returned by getenv()
can result in overwritten environmental data.
Recommendation |
Severity |
Likelihood |
Remediation Cost |
Priority |
Level |
---|---|---|---|---|---|
ENV00-A |
low |
probable |
medium |
P4 |
L3 |
Related Vulnerabilities
Search for vulnerabilities resulting from the violation of this rule on the CERT website.
References
[[ISO/IEC 9899:1999]] Section 7.20.4, "Communication with the environment"
[[ISO/IEC PDTR 24731-2]]
[[MSDN]] _dupenv_s()
and _wdupenv_s()
, getenv_s()
, _wgetenv_s()
[[Open Group 04]] Chapter 8, "Environment Variables", strdup
[[Viega 03]] Section 3.6, "Using Environment Variables Securely"
10. Environment (ENV) 10. Environment (ENV) ENV01-A. Do not make assumptions about the size of an environment variable