You've already forked fido2-manage.cpp
mirror of
https://github.com/token2/fido2-manage.cpp.git
synced 2026-06-22 14:42:52 -07:00
Merge pull request #22 from allamiro/bugfix/issue-2-escape-wrong-layer-createprocess
Fix #2 - replace CMD shell escaping with correct CreateProcess quoting
This commit is contained in:
+37
-53
@@ -26,42 +26,32 @@ struct PasskeyInfo {
|
||||
std::wstring domain;
|
||||
};
|
||||
|
||||
std::wstring EscapeCommandLineArgument(const std::wstring& input) {
|
||||
std::wstring escaped = L"\""; // Start with a quote to properly handle spaces
|
||||
// Quotes one argument for a CreateProcess command string using the
|
||||
// Windows C-runtime parsing rules: wrap in double quotes, double any
|
||||
// backslashes that immediately precede a double quote or the closing
|
||||
// quote, and escape embedded double quotes with a backslash.
|
||||
// Do NOT use this for cmd.exe shell strings (ShellExecute paths).
|
||||
std::wstring QuoteArg(const std::wstring& arg) {
|
||||
std::wstring result = L"\"";
|
||||
int backslashes = 0;
|
||||
|
||||
for (wchar_t ch : input) {
|
||||
switch (ch) {
|
||||
case L'"': escaped += L"\\\""; break; // Escape double quotes
|
||||
case L'^':
|
||||
case L'&':
|
||||
case L'|':
|
||||
case L'<':
|
||||
case L'>':
|
||||
case L'%':
|
||||
case L'!':
|
||||
case L'(':
|
||||
case L')':
|
||||
case L'=':
|
||||
case L';':
|
||||
case L'`':
|
||||
case L',':
|
||||
case L'[':
|
||||
case L']':
|
||||
case L'{':
|
||||
case L'}':
|
||||
case L'*':
|
||||
case L'?':
|
||||
case L'\\': // Escape special characters for CMD
|
||||
escaped += L'^';
|
||||
escaped += ch;
|
||||
break;
|
||||
default:
|
||||
escaped += ch;
|
||||
for (wchar_t c : arg) {
|
||||
if (c == L'\\') {
|
||||
backslashes++;
|
||||
} else if (c == L'"') {
|
||||
result.append(backslashes * 2, L'\\');
|
||||
result += L"\\\"";
|
||||
backslashes = 0;
|
||||
} else {
|
||||
result.append(backslashes, L'\\');
|
||||
result += c;
|
||||
backslashes = 0;
|
||||
}
|
||||
}
|
||||
|
||||
escaped += L"\""; // End with a quote
|
||||
return escaped;
|
||||
result.append(backslashes * 2, L'\\');
|
||||
result += L'"';
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +130,7 @@ std::vector<PasskeyInfo> ParseResidentKeys(const std::vector<std::wstring>& outp
|
||||
|
||||
std::vector<std::wstring> GetDomains(const std::wstring& deviceNumber, const std::wstring& globalPin) {
|
||||
// Command for the first run
|
||||
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber + L" -pin " + globalPin;
|
||||
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
|
||||
|
||||
// Debug: Show the command being executed
|
||||
//MessageBox(NULL, command.c_str(), L"Debug: Command Being Executed", MB_OK);
|
||||
@@ -171,9 +161,9 @@ std::vector<PasskeyInfo> GetResidentKeysWithDomains(const std::wstring& deviceNu
|
||||
|
||||
for (const auto& domain : domains) {
|
||||
// Command for the second run
|
||||
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber +
|
||||
L" -domain " + domain + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> output = RunCommandAndGetOutputWithTimeout(command, DEVICE_TIMEOUT_MS);
|
||||
std::wstring command = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) +
|
||||
L" -domain " + QuoteArg(domain) + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> output = RunCommandAndGetOutput(command);
|
||||
|
||||
// Debug: Check the raw output
|
||||
for (const auto& line : output) {
|
||||
@@ -305,8 +295,8 @@ INT_PTR CALLBACK PasskeysDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPAR
|
||||
|
||||
|
||||
// Step 1: First Run - Fetch domains
|
||||
std::wstring firstCommand = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> firstOutput = RunCommandAndGetOutputWithTimeout(firstCommand, DEVICE_TIMEOUT_MS);
|
||||
std::wstring firstCommand = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> firstOutput = RunCommandAndGetOutput(firstCommand);
|
||||
|
||||
// Parse the output to get domains (users)
|
||||
std::vector<std::wstring> domains;
|
||||
@@ -327,9 +317,9 @@ INT_PTR CALLBACK PasskeysDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPAR
|
||||
// Step 2: Second Run - Fetch resident keys for each domain
|
||||
std::vector<PasskeyInfo> refreshedPasskeys;
|
||||
for (const auto& domain : domains) {
|
||||
std::wstring secondCommand = L".\\fido2-manage.exe -residentkeys -device " + deviceNumber +
|
||||
L" -domain " + domain + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> secondOutput = RunCommandAndGetOutputWithTimeout(secondCommand, DEVICE_TIMEOUT_MS);
|
||||
std::wstring secondCommand = L".\\fido2-manage.exe -residentkeys -device " + QuoteArg(deviceNumber) +
|
||||
L" -domain " + QuoteArg(domain) + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> secondOutput = RunCommandAndGetOutput(secondCommand);
|
||||
|
||||
// Parse the second run output
|
||||
std::vector<PasskeyInfo> parsedPasskeys = ParseResidentKeys(secondOutput);
|
||||
@@ -465,8 +455,8 @@ INT_PTR CALLBACK FingerprintDialogProc(HWND hDlg, UINT message, WPARAM wParam, L
|
||||
SendMessage(hListView, LVM_INSERTCOLUMN, 1, (LPARAM)&lvColumn);
|
||||
|
||||
// Populate the ListView with fingerprints
|
||||
std::wstring command = L".\\fido2-manage.exe -fingerprintlist -device " + deviceNumber + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> output = RunCommandAndGetOutputWithTimeout(command, DEVICE_TIMEOUT_MS);
|
||||
std::wstring command = L".\\fido2-manage.exe -fingerprintlist -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
|
||||
std::vector<std::wstring> output = RunCommandAndGetOutput(command);
|
||||
|
||||
for (const auto& line : output) {
|
||||
// Split the line into parts
|
||||
@@ -704,14 +694,6 @@ void PopulateListView(HWND hwnd, const std::wstring& deviceNumber) {
|
||||
// Show the input box and store the result in globalPIN
|
||||
globalPin = ShowInputBox(NULL, L"Enter PIN", L"Please enter your PIN:");
|
||||
|
||||
if (globalPin.empty()) {
|
||||
// User cancelled the dialog — reset UI silently
|
||||
RefreshData();
|
||||
return;
|
||||
}
|
||||
|
||||
globalPin = EscapeCommandLineArgument(globalPin);
|
||||
|
||||
const size_t MIN_PIN_LENGTH = 4;
|
||||
|
||||
if (globalPin.length() < MIN_PIN_LENGTH) {
|
||||
@@ -722,9 +704,11 @@ void PopulateListView(HWND hwnd, const std::wstring& deviceNumber) {
|
||||
return;
|
||||
}
|
||||
|
||||
globalPin = QuoteArg(globalPin);
|
||||
|
||||
|
||||
// Construct the command
|
||||
std::wstring command = L".\\fido2-manage.exe -storage -device " + deviceNumber + L" -pin " + globalPin;
|
||||
std::wstring command = L".\\fido2-manage.exe -storage -device " + QuoteArg(deviceNumber) + L" -pin " + globalPin;
|
||||
|
||||
// Execute the command
|
||||
std::vector<std::wstring> output = RunCommandAndGetOutputWithTimeout(command, DEVICE_TIMEOUT_MS);
|
||||
@@ -751,7 +735,7 @@ void PopulateListView(HWND hwnd, const std::wstring& deviceNumber) {
|
||||
|
||||
}
|
||||
// Construct the second command
|
||||
std::wstring command2 = L".\\fido2-manage.exe -info -device " + deviceNumber;
|
||||
std::wstring command2 = L".\\fido2-manage.exe -info -device " + QuoteArg(deviceNumber);
|
||||
|
||||
// Execute the second command
|
||||
std::vector<std::wstring> additionalOutput = RunCommandAndGetOutputWithTimeout(command2, DEVICE_TIMEOUT_MS);
|
||||
|
||||
Reference in New Issue
Block a user