Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/client/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
#endif
#include <wolfssl/wolfcrypt/settings.h>

#include <ctype.h>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 [Low] #include <ctype.h> placed between wolfSSL headers
🔧 NIT convention

The new #include <ctype.h> is placed on line 37 between wolfssl/wolfcrypt/settings.h (line 35) and wolfssl/ssl.h (line 39). In wolfSSL convention, settings.h is typically followed immediately by other wolfSSL headers since it sets up preprocessor macros those headers depend on. System includes are conventionally grouped together. The include is needed (for the pre-existing isalpha() call on line 142), so adding it explicitly is correct — only the placement is slightly off.

Suggestion:

Suggested change
#include <ctype.h>
Move `#include <ctype.h>` to after `#include <wolfssl/ssl.h>` or group it with the other system includes near the top of the file (around the `#include <config.h>` block).


#include <wolfssl/ssl.h>

#include <wolfclu/clu_header_main.h>
Expand Down Expand Up @@ -147,6 +149,21 @@ static WC_INLINE void clu_build_addr(SOCKADDR_IN4_T* addr, SOCKADDR_IN6_T* ipv6,
FILE* fp;
char host_out[100];
char cmd[100];
const char* cp;

/* Validate hostname: only allow characters valid in DNS names
* (RFC 1123) to prevent shell injection via popen().
* Use explicit ASCII ranges instead of isalnum() to avoid
* locale-dependent behavior. */
for (cp = peer; *cp != '\0'; cp++) {
if (!((*cp >= 'A' && *cp <= 'Z') ||
(*cp >= 'a' && *cp <= 'z') ||
(*cp >= '0' && *cp <= '9') ||
*cp == '.' || *cp == '-')) {
err_sys("invalid character in hostname");
return;
}
}

XSTRNCPY(cmd, "host ", 6);
XSTRNCAT(cmd, peer, 99 - XSTRLEN(cmd));
Expand Down
34 changes: 34 additions & 0 deletions tests/client/client-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,39 @@ fi

rm tmp.crt

# Regression tests: shell injection via hostname must not execute injected command.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 [Medium] Missing backtick command-substitution injection test case
💡 SUGGEST test

The regression tests cover three shell injection vectors: semicolons (;), dollar-paren command substitution ($()), and pipes (|). However, backtick command substitution (`cmd`) is a fourth classic shell injection vector that is not tested. While the allowlist validation in client.c blocks backticks (they are not in [A-Za-z0-9.-]), adding a test for completeness would strengthen the regression suite and document the intent. An & (background execution) test would also be a nice-to-have.

Suggestion:

Suggested change
# Regression tests: shell injection via hostname must not execute injected command.
# Backtick command substitution: "`touch clu_injection_probe.txt`" passed as peer
./wolfssl s_client -connect 'evil`touch clu_injection_probe.txt`.com:443' \
2>/dev/null
if [ -f "$INJFILE" ]; then
echo "SECURITY FAILURE: command injection via hostname (backtick)"
rm -f "$INJFILE"
exit 99
fi

# Applies to the WOLFSSL_USE_POPEN_HOST path where peer is concatenated into a
# popen() shell command. On other builds, getaddrinfo/gethostbyname reject
# these hostnames before any shell is involved, so the tests pass either way.
INJFILE="clu_injection_probe.txt"
rm -f "$INJFILE"

# Semicolon: "evil.com;touch clu_injection_probe.txt" passed as peer
./wolfssl s_client -connect 'evil.com;touch clu_injection_probe.txt:443' \
2>/dev/null
if [ -f "$INJFILE" ]; then
echo "SECURITY FAILURE: command injection via hostname (semicolon)"
rm -f "$INJFILE"
exit 99
fi

# Command substitution: "$(touch clu_injection_probe.txt)" passed as peer
./wolfssl s_client -connect 'evil$(touch clu_injection_probe.txt).com:443' \
2>/dev/null
if [ -f "$INJFILE" ]; then
echo "SECURITY FAILURE: command injection via hostname (command substitution)"
rm -f "$INJFILE"
exit 99
fi

# Pipe: "evil.com|touch clu_injection_probe.txt" passed as peer
./wolfssl s_client -connect 'evil.com|touch clu_injection_probe.txt:443' \
2>/dev/null
if [ -f "$INJFILE" ]; then
echo "SECURITY FAILURE: command injection via hostname (pipe)"
rm -f "$INJFILE"
exit 99
fi

echo "Done"
exit 0
Loading