How to fetch RSA public key modulus and exponent in C/C++? - c++

I'm using the BCrypt Windows library to handle the RSA algorithm in my application.
Problem : I need to fetch the RSA public key modulus and exponent.
In C# language, I was using the RSACryptoProvider class to fetch these informations (ExportParameters method).
In my C/C++ application, BCrypt seems to be unable to fetch the right data...
What I've done so far:
BOOL RSA_New(RSA_CTX_ST* rsa_ctx, const BYTE key_data[256], DWORD key_len, BOOL cipher)
{
if (rsa_ctx == NULL || key_len != RSA_2048_BLOCK_SIZE)
{
return FALSE;
}
NTSTATUS status = STATUS_UNSUCCESSFUL;
BCRYPT_ALG_HANDLE* algo_handle = NULL;
BCRYPT_KEY_HANDLE* key_handle = NULL;
algo_handle = malloc(sizeof(BCRYPT_ALG_HANDLE));
key_handle = malloc(sizeof(BCRYPT_KEY_HANDLE));
if (algo_handle == NULL || key_handle == NULL) goto END;
//Creation handle Algo
if (!NT_SUCCESS(status = BCryptOpenAlgorithmProvider(algo_handle, BCRYPT_RSA_ALGORITHM, NULL, 0)))
{
UTL_Trace("SEC", VRB_MAJOR, "RSA:Algorithm handle opening error");
if (status == STATUS_INVALID_HANDLE)
{
UTL_Trace("SEC", VRB_INFO, "INVALID HANDLE");
}
if (status == STATUS_INVALID_PARAMETER)
{
UTL_Trace("SEC", VRB_INFO, "INVALID PARAMS");
}
goto END;
}
// Key pair generation
if (!NT_SUCCESS(status = BCryptGenerateKeyPair(algo_handle, key_handle, 2048, 0)))
{
UTL_Trace("SEC", VRB_MAJOR, "RSA:Key pair generating error");
if (status == STATUS_INVALID_HANDLE)
{
UTL_Trace("SEC", VRB_INFO, "INVALID HANDLE");
}
if (status == STATUS_INVALID_PARAMETER)
{
UTL_Trace("SEC", VRB_INFO, "INVALID PARAMS");
}
goto END;
}
BCRYPT_RSAKEY_BLOB my_rsa_blob;
ULONG* pcbResult = NULL;
pcbResult = calloc(1, sizeof(ULONG));
if (pcbResult == NULL)
{
UTL_Trace("SEC", VRB_INFO, "Allocating RSA result error");
goto END;
}
// Export parameters of keys
if (!NT_SUCCESS(status = BCryptExportKey(key_handle, NULL, BCRYPT_RSAFULLPRIVATE_BLOB, &my_rsa_blob, sizeof(my_rsa_blob), pcbResult, 0)))
{
UTL_Trace("SEC", VRB_MAJOR, "RSA:Key pair exporting error");
if (status == STATUS_INVALID_HANDLE)
{
UTL_Trace("SEC", VRB_INFO, "INVALID HANDLE");
}
if (status == STATUS_INVALID_PARAMETER)
{
UTL_Trace("SEC", VRB_INFO, "INVALID PARAMS");
}
goto END;
}
return TRUE;
END:
if (algo_handle != NULL) free(algo_handle);
if (key_handle != NULL) free(key_handle);
return FALSE;
}
In my_rsa_blob, I should have the size of the modulus and exponent but not their values...
Does anyone have a solution to this problem ?

In fact, I did find a solution !
With the call of BCryptExportKey, the variable my_rsa_blob should have been a PUCHAR variable, which is a pointer to a string.
With this, I found a link in Microsoft Docs while searching...
The link is showing how the PUCHAR variable is designed :
https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/540b7b8b-2232-45c8-9d7c-af7a5d5218ed
After, you can try to manipulate data like this :
static BOOL RSA_RecoverKeyInformation(RSA_CTX_ST* rsa_ctx, PUCHAR ptrKey)
{
strncpy(rsa_ctx->key_information.header.Magic, ptrKey[0], 4);
strncpy(rsa_ctx->key_information.header.BitLength, ptrKey[1 * 4], 4);
strncpy(rsa_ctx->key_information.header.cbPublicExp, ptrKey[2 * 4], 4);
strncpy(rsa_ctx->key_information.header.cbModulus, ptrKey[3 * 4], 4);
strncpy(rsa_ctx->key_information.header.cbPrime1, ptrKey[4 * 4], 4);
strncpy(rsa_ctx->key_information.header.cbPrime2, ptrKey[5 * 4], 4);
size_t sizeOfModulus = rsa_ctx->key_information.header.cbModulus;
size_t sizeOfExponent = rsa_ctx->key_information.header.cbPublicExp;
rsa_ctx->key_information.keyExponent = malloc(sizeOfExponent);
rsa_ctx->key_information.keyModulus = malloc(sizeOfModulus);
if (rsa_ctx->key_information.keyExponent == NULL || rsa_ctx->key_information.keyModulus == NULL) goto END;
strncpy(rsa_ctx->key_information.keyExponent, ptrKey[6 * 4], sizeOfExponent);
strncpy(rsa_ctx->key_information.keyModulus, ptrKey[6 * 4 + sizeOfExponent], sizeOfModulus);
return TRUE;
END:
if (rsa_ctx->key_information.keyExponent != NULL) free(rsa_ctx->key_information.keyExponent);
if (rsa_ctx->key_information.keyModulus != NULL) free(rsa_ctx->key_information.keyModulus);
return FALSE;
}

Related

wintun:ERROR_INVALID_PARAMETER on registering ring buffers

I am currently trying to get the wintun driver to work with my program for simple tunneling (see: https://www.wintun.net/ ).
I successfully find and open the network device, but when it comes to registering the buffer, I get the result ERROR_INVALID_PARAMETER (87). Like I said, opening works just fine and registering is done with SYSTEM privileges (if this is not done, I get ERROR_ACCESS_DENIED (5)).
First attempt was to malloc the ring buffers, but after that did not work I looked at how OpenVPN does it (yes, it added wintun support) and they seem to do with with CreateFileMapping.
First of all, here is my struct:
typedef struct _TUN_RING {
volatile ULONG Head;
volatile ULONG Tail;
volatile LONG Alertable;
UCHAR Data[(1024 * 1024) + 0x10000];
} TUN_RING;
which is according to the docs (https://git.zx2c4.com/wintun/about/ section "Ring Layout). Also its the same as OpenVPN does.
After that I create the file mapping
send_ring_handle_ = CreateFileMapping(INVALID_HANDLE_VALUE,
nullptr,
PAGE_READWRITE,
0,
sizeof(TUN_RING),
nullptr);
recv_ring_handle_ = CreateFileMapping(INVALID_HANDLE_VALUE,
nullptr,
PAGE_READWRITE,
0,
sizeof(TUN_RING),
nullptr);
Then I create the mappings:
send_ring_ = (TUN_RING *)MapViewOfFile(send_ring_handle_,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof(TUN_RING));
recv_ring_ = (TUN_RING *)MapViewOfFile(recv_ring_handle_,
FILE_MAP_ALL_ACCESS,
0,
0,
sizeof(TUN_RING));
and finally (after impersonating the system user) trying to register it with DeviceIoControl:
TUN_REGISTER_RINGS reg_rings;
memset(&reg_rings, 0, sizeof(TUN_REGISTER_RINGS));
reg_rings.Send.RingSize = sizeof(TUN_RING);
reg_rings.Send.Ring = send_ring_;
reg_rings.Send.TailMoved = CreateEvent(0, TRUE, FALSE, 0);
reg_rings.Receive.RingSize = sizeof(TUN_RING);
reg_rings.Receive.Ring = recv_ring_;
reg_rings.Receive.TailMoved = CreateEvent(0, TRUE, FALSE, 0);
DWORD len;
if (!DeviceIoControl(tun_fd_,
TUN_IOCTL_REGISTER_RINGS,
&reg_rings,
sizeof(reg_rings),
nullptr,
0,
&len,
nullptr))
{
printf("Could not register ring buffers (%d).", ::GetLastError());
return false;
}
Can anybody point me to where I am wrong? Like I said, with malloc instead of the file mapping the same error arieses.
I have written a complete example by now using malloc:
#include <windows.h>
#include <winioctl.h>
#include <IPHlpApi.h>
#include <ndisguid.h>
#include <TlHelp32.h>
#include <tchar.h>
#include <securitybaseapi.h>
#include <cfgmgr32.h>
#include <stdint.h>
#include <stdio.h>
#include <string>
#include <assert.h>
#pragma pack(push, 1)
typedef struct _TUN_PACKET_PROTO {
ULONG Size;
UCHAR Data[]; // max packet size as defined by the driver.
} TUN_PACKET_PROTO;
typedef struct _TUN_RING_PROTO {
volatile ULONG Head;
volatile ULONG Tail;
volatile LONG Alertable;
UCHAR Data[];
} TUN_RING_PROTO;
#define TUN_IOCTL_REGISTER_RINGS CTL_CODE(51820U, 0x970U, METHOD_BUFFERED, FILE_READ_DATA | FILE_WRITE_DATA)
#define TUN_IOCTL_FORCE_CLOSE_HANDLES CTL_CODE(51820U, 0x971U, METHOD_NEITHER, FILE_READ_DATA | FILE_WRITE_DATA)
#define WINTUN_RING_CAPACITY 0x800000
#define WINTUN_RING_TRAILING_BYTES 0x10000
#define WINTUN_MAX_PACKET_SIZE 0xffff
#define WINTUN_PACKET_ALIGN 4
/* Memory alignment of packets and rings */
#define TUN_ALIGNMENT sizeof(ULONG)
#define TUN_ALIGN(Size) (((ULONG)(Size) + ((ULONG)TUN_ALIGNMENT - 1)) & ~((ULONG)TUN_ALIGNMENT - 1))
#define TUN_IS_ALIGNED(Size) (!((ULONG)(Size) & ((ULONG)TUN_ALIGNMENT - 1)))
/* Maximum IP packet size */
#define TUN_MAX_IP_PACKET_SIZE 0xFFFF
/* Maximum packet size */
#define TUN_MAX_PACKET_SIZE TUN_ALIGN(sizeof(TUN_PACKET_PROTO) + TUN_MAX_IP_PACKET_SIZE)
/* Minimum ring capacity. */
#define TUN_MIN_RING_CAPACITY 0x20000 /* 128kiB */
/* Maximum ring capacity. */
#define TUN_MAX_RING_CAPACITY 0x4000000 /* 64MiB */
/* Calculates ring capacity */
#define TUN_RING_CAPACITY(Size) ((Size) - sizeof(TUN_RING_PROTO) - (TUN_MAX_PACKET_SIZE - TUN_ALIGNMENT))
/* Calculates ring offset modulo capacity */
#define TUN_RING_WRAP(Value, Capacity) ((Value) & (Capacity - 1))
#define IS_POW2(x) ((x) && !((x) & ((x)-1)))
typedef struct _TUN_RING {
volatile ULONG Head;
volatile ULONG Tail;
volatile LONG Alertable;
UCHAR Data[WINTUN_RING_CAPACITY + (TUN_MAX_PACKET_SIZE-TUN_ALIGNMENT)];
} TUN_RING;
typedef struct _TUN_PACKET {
ULONG Size;
UCHAR Data[WINTUN_MAX_PACKET_SIZE]; // max packet size as defined by the driver.
} TUN_PACKET;
typedef struct _TUN_REGISTER_RINGS {
struct {
ULONG RingSize;
TUN_RING *Ring;
HANDLE TailMoved;
} Send, Receive;
} TUN_REGISTER_RINGS;
#pragma pack(pop)
class regkey_t
{
public:
regkey_t(void);
regkey_t(HKEY handle);
~regkey_t(void);
void attach(HKEY handle);
void release(void);
HKEY detach(void);
operator HKEY (void) const;
HKEY &get(void);
HKEY *operator &(void);
private:
regkey_t(const regkey_t &);
regkey_t &operator = (const regkey_t &);
HKEY handle_;
};
regkey_t::regkey_t():
handle_(0)
{
}
regkey_t::regkey_t(HKEY handle):
handle_(handle)
{
}
regkey_t::~regkey_t(void)
{
release();
}
void regkey_t::attach(HKEY handle)
{
release();
handle_ = handle;
}
void regkey_t::release(void)
{
if (handle_)
{
const LONG res (::RegCloseKey(handle_));
if (res != ERROR_SUCCESS)
{
printf("Couldn't close a reg handle (%lu).\n", res);
}
handle_ = 0;
}
}
HKEY regkey_t::detach(void)
{
const HKEY result (handle_);
handle_ = 0;
return result;
}
HKEY &regkey_t::get(void)
{
return handle_;
}
HKEY *regkey_t::operator &(void)
{
return &handle_;
}
regkey_t::operator HKEY(void) const
{
return handle_;
}
bool impersonate_as_system()
{
HANDLE thread_token, process_snapshot, winlogon_process, winlogon_token, duplicated_token;
PROCESSENTRY32 entry;
BOOL ret;
DWORD pid = 0;
TOKEN_PRIVILEGES privileges;
::memset(&entry, 0, sizeof(entry));
::memset(&privileges, 0, sizeof(privileges));
entry.dwSize = sizeof(PROCESSENTRY32);
privileges.PrivilegeCount = 1;
privileges.Privileges->Attributes = SE_PRIVILEGE_ENABLED;
if (!LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &privileges.Privileges[0].Luid))
{
return false;
}
if (!ImpersonateSelf(SecurityImpersonation))
{
return false;
}
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES, FALSE, &thread_token))
{
RevertToSelf();
return false;
}
if (!AdjustTokenPrivileges(thread_token, FALSE, &privileges, sizeof(privileges), NULL, NULL))
{
CloseHandle(thread_token);
RevertToSelf();
return false;
}
CloseHandle(thread_token);
process_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (process_snapshot == INVALID_HANDLE_VALUE)
{
RevertToSelf();
return false;
}
for (ret = Process32First(process_snapshot, &entry); ret; ret = Process32Next(process_snapshot, &entry))
{
if (::strcmp(entry.szExeFile, "winlogon.exe") == 0)
{
pid = entry.th32ProcessID;
break;
}
}
CloseHandle(process_snapshot);
if (!pid)
{
RevertToSelf();
return false;
}
winlogon_process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
if (!winlogon_process)
{
RevertToSelf();
return false;
}
if (!OpenProcessToken(winlogon_process, TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &winlogon_token))
{
CloseHandle(winlogon_process);
RevertToSelf();
return false;
}
CloseHandle(winlogon_process);
if (!DuplicateToken(winlogon_token, SecurityImpersonation, &duplicated_token))
{
CloseHandle(winlogon_token);
RevertToSelf();
return false;
}
CloseHandle(winlogon_token);
if (!SetThreadToken(NULL, duplicated_token))
{
CloseHandle(duplicated_token);
RevertToSelf();
return false;
}
CloseHandle(duplicated_token);
return true;
}
std::string get_instance_id(uint32_t device_index)
{
const std::string key_name("SYSTEM\\CurrentControlSet\\Control\\Class\\{4D36E972-E325-11CE-BFC1-08002BE10318}");
std::string device_id("");
regkey_t adapters;
DWORD ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, key_name.c_str(), 0, KEY_READ, &adapters);
if (ret != ERROR_SUCCESS)
{
printf("Could not open registry key %s (%d).\n", key_name.c_str(), ret);
return device_id;
}
DWORD sub_keys(0);
ret = ::RegQueryInfoKey(adapters, NULL, NULL, NULL, &sub_keys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS)
{
printf("Could not get info from %s (%d).\n", key_name.c_str(), ret);
return device_id;
}
if (sub_keys <= 0)
{
printf("Wrong registry key %s.\n", key_name.c_str());
return device_id;
}
if (device_index >= sub_keys)
{
return device_id;
}
uint32_t index(0);
for (DWORD i = 0; i < sub_keys; i++)
{
const uint32_t max_key_length = 255;
TCHAR key[max_key_length];
DWORD keylen(max_key_length);
// Get the adapter name
ret = ::RegEnumKeyEx(adapters, i, key, &keylen, NULL, NULL, NULL, NULL);
if (ret != ERROR_SUCCESS)
{
continue;
}
// Append it to NETWORK_ADAPTERS and open it
regkey_t device;
const std::string new_key(key_name + "\\" + std::string(key));
ret = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, new_key.c_str(), 0, KEY_READ, &device);
if (ret != ERROR_SUCCESS)
{
continue;
}
TCHAR data[256];
DWORD len(sizeof(data));
ret = ::RegQueryValueEx(device, "ComponentId", NULL, NULL, (LPBYTE)data, &len);
if (ret != ERROR_SUCCESS)
{
continue;
}
std::string device_name("wintun");
if (::_tcsnccmp(data, device_name.c_str(), sizeof(TCHAR) * device_name.length()) == 0)
{
if (device_index != index)
{
index++;
continue;
}
DWORD type;
len = sizeof(data);
ret = ::RegQueryValueEx(device, "DeviceInstanceID", NULL, &type, (LPBYTE)data, &len);
if (ret != ERROR_SUCCESS)
{
printf("Could not get info from %s (%d).\n", key_name.c_str(), ret);
}
device_id = data;
break;
}
}
return device_id;
}
bool open_tun_device()
{
HANDLE tun_fd_ = INVALID_HANDLE_VALUE;
std::string device_id;
uint32_t device_index;
{
TCHAR *interface_list = nullptr;
for (device_index = 0; device_index < 256; ++device_index)
{
device_id = get_instance_id(device_index);
if (device_id.empty())
{
continue;
}
CONFIGRET status = CR_SUCCESS;
// This loop is recommended as "robust code" by MSDN. See the Remarks of CM_Get_Device_Interface_list.
do
{
DWORD required_chars(0);
if ((status = ::CM_Get_Device_Interface_List_Size(&required_chars,
(LPGUID)&GUID_DEVINTERFACE_NET,
(char *)device_id.c_str(),
CM_GET_DEVICE_INTERFACE_LIST_PRESENT)) != CR_SUCCESS || !required_chars)
{
break;
}
assert(required_chars > 0);
interface_list = (TCHAR *)::malloc(sizeof(TCHAR) * required_chars);
status = ::CM_Get_Device_Interface_List((LPGUID)&GUID_DEVINTERFACE_NET,
(char *)device_id.c_str(),
interface_list,
required_chars,
CM_GET_DEVICE_INTERFACE_LIST_PRESENT);
if (status == CR_SUCCESS)
{
break;
}
::free(interface_list);
interface_list = nullptr;
} while(status == CR_BUFFER_SMALL);
if (interface_list)
{
break;
}
}
if (!interface_list)
{
printf("Could not find wintun interface.\n");
return false;
}
else
{
tun_fd_ = ::CreateFile(interface_list,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr,
OPEN_EXISTING, 0, nullptr);
}
::free(interface_list);
}
if (!tun_fd_ || tun_fd_ == INVALID_HANDLE_VALUE)
{
printf("Could not open wintun device.\n");
return false;
}
printf("Opened wintun device.\n");
::Sleep(1000);
TUN_RING * send_ring_ = (TUN_RING *)::malloc(sizeof(TUN_RING));
TUN_RING * recv_ring_ = (TUN_RING *)::malloc(sizeof(TUN_RING));
if (!recv_ring_ || !send_ring_)
{
printf("Could not malloc.\n");
return false;
}
::memset(send_ring_, 0, sizeof(*send_ring_));
::memset(recv_ring_, 0, sizeof(*recv_ring_));
recv_ring_->Alertable = TRUE;
recv_ring_->Head = 0;
recv_ring_->Tail = 0;
send_ring_->Alertable = TRUE;
send_ring_->Head = 0;
send_ring_->Tail = 0;
HANDLE send_event = ::CreateEvent(0, FALSE, FALSE, 0);
HANDLE recv_event = ::CreateEvent(0, FALSE, FALSE, 0);
// register the rings
if (impersonate_as_system())
{
TUN_REGISTER_RINGS reg_rings;
::memset(&reg_rings, 0, sizeof(TUN_REGISTER_RINGS));
reg_rings.Send.RingSize = sizeof(TUN_RING);
reg_rings.Send.Ring = send_ring_;
reg_rings.Send.TailMoved = send_event;
reg_rings.Receive.RingSize = sizeof(TUN_RING);
reg_rings.Receive.Ring = recv_ring_;
reg_rings.Receive.TailMoved = recv_event;
int send_capacity = TUN_RING_CAPACITY(reg_rings.Send.RingSize);
if ((send_capacity < TUN_MIN_RING_CAPACITY || send_capacity > TUN_MAX_RING_CAPACITY ||
!IS_POW2(send_capacity) || !reg_rings.Send.TailMoved || !reg_rings.Send.Ring))
{
printf("Fuck this shit I am out...\n");
}
DWORD len;
DWORD fuckyou = 0;
if (!::DeviceIoControl(tun_fd_, TUN_IOCTL_FORCE_CLOSE_HANDLES,
&fuckyou, sizeof(fuckyou), nullptr, 0, &len, nullptr))
{
printf("Error releasing handles (%d).\n", ::GetLastError());
}
if (!::DeviceIoControl(tun_fd_,
TUN_IOCTL_REGISTER_RINGS,
&reg_rings,
sizeof(reg_rings),
nullptr,
0,
&len,
nullptr))
{
printf("Could not register ring buffers (%d).\n", ::GetLastError());
::Sleep(10000);
RevertToSelf();
return false;
}
::Sleep(10000);
RevertToSelf();
}
else
{
printf("Could not elevate to SYSTEM\n");
return false;
}
return true;
}
int main()
{
if (!open_tun_device())
{
printf("Experiment failed.\n");
}
printf("Size TUNRING: %d (%d)\n", sizeof(TUN_RING), 0x800000 + 0x010000 + 0x0C);
printf("Capacity: %d\n", TUN_RING_CAPACITY(sizeof(TUN_RING)));
if (!IS_POW2(TUN_RING_CAPACITY(sizeof(TUN_RING))))
{
printf("Shit gone wrong...\n");
}
return 0;
}
Please make sure to RUN THIS ELEVATED or you will get error 5 ERROR_ACCESS_DENIED.
I can see a difference in your code when registering rings.
You are doing:
reg_rings.Send.RingSize = sizeof(TUN_RING);
reg_rings.Receive.RingSize = sizeof(TUN_RING);
While the docs says:
Send.RingSize, Receive.RingSize: Sizes of the rings (sizeof(TUN_RING) + capacity + 0x10000, as above)
Your ring is sizeof(TUN_RING) + UCHAR[(1024 * 1024) + 0x10000]
I guess it can't accept a ring that has no data space?
Sorry, I see your TUN_RING includes de data...
May be the events aren't good:
If an event is created from a service or a thread that is impersonating a different user, you can either apply a security descriptor to the event when you create it, or change the default security descriptor for the creating process by changing its default DACL
reg_rings.Send.TailMoved = CreateEvent(0, TRUE, FALSE, 0);
reg_rings.Receive.TailMoved = CreateEvent(0, TRUE, FALSE, 0);
You seem to be using the default DACL.
There may be aligning problems. If malloc isn't returning an aligned address for your buffer (as may be in debug mode, because there are memory management bytes) your Data member for the packet could be not aligned.
You can check the alignment against the address:
template <unsigned int alignment>
struct IsAligned
{
static_assert((alignment & (alignment - 1)) == 0, "Alignment must be a power of 2");
static inline bool Value(const void * ptr)
{
return (((uintptr_t)ptr) & (alignment - 1)) == 0;
}
};
std::cout << IsAligned<32>::Value(ptr + i) << std::endl;
Giving the first packet address &(TUN_RING.Data[0]) (I guess.)
As said in your comment, it is the case, it is unaligned.
You can try two things.
First reserve memory with aligned_alloc which will give you an aligned buffer for TUN_RING.
Second, if TUN_RING is already aligned and the packet alignment is the problem, then you should give the correct offset to the head and tail:
recv_ring_->Head = 0; // <- aligned byte offset
recv_ring_->Tail = 0;
send_ring_->Head = 0;
send_ring_->Tail = 0;
Remember:
Head: Byte offset of the first packet in the ring. Its value must be a multiple of 4 and less than ring capacity.
Tail: Byte offset of the start of free space in the ring. Its value must be multiple of 4 and less than ring capacity.
The byte offset must be a multiple of 4.
You have to increment those skipped bytes to the buffer size. For that you may need to allocate extra space that won't be used, but I think it won't be too much.
In a second view to events, in the docs it says event has to be auto-reset:
Send.TailMoved: A handle to an auto-reset event created by the client that Wintun signals after it moves the Tail member of the send ring.
Receive.TailMoved: A handle to an auto-reset event created by the client that the client will signal when it changes Receive.Ring->Tail and Receive.Ring->Alertable is non-zero.
In your example, the event is auto-reset:
HANDLE send_event = ::CreateEvent(0, FALSE, FALSE, 0);
HANDLE recv_event = ::CreateEvent(0, FALSE, FALSE, 0);
but in the code you show (at top of question) isn't:
reg_rings.Send.TailMoved = CreateEvent(0, TRUE, FALSE, 0);
reg_rings.Receive.TailMoved = CreateEvent(0, TRUE, FALSE, 0);
I don't know if the parameter checking goes so far as to verify event auto-reset setting (not even if that's possible.) Moreover, the openvpn code creates them non auto-reset (although may be there is some code around to signal them before registering.)
Okay, after a lot of trial and error I have translated the whole setup routine from the WireGuard Go code (see here: https://github.com/WireGuard/wireguard-go ) to C++, which seems to make it work. It accepts the rings now just as in the first post and the device is shown as connected afterwards...
They are doing some registry tweaks after installing the device (see https://github.com/WireGuard/wireguard-go/blob/4369db522b3fd7adc28a2a82b89315a6f3edbcc4/tun/wintun/wintun_windows.go#L207 ) which I think takes the cake. Thanks for everyone helping in finding this.
For me the fix to get rid off ERROR_INVALID_PARAMETER (87) was to switch from x86 to x64 architecture in Visual Studio
The issue here is the alignment of the structs. You align your structs to 1 byte [#pragma pack(push, 1)] while the wintun driver does 8(/ZP8 in the solution). This will result in differing struct sizes and thus the size checks will fall through. Furthermore I would like to recommend that you use VirtualAlloc or the Mapping instead of malloc.

OpenCL CL_INVALID_COMMAND_QUEUE when launching Kernel at Both NVIDIA and Intel GPUs

This might not the the most narrowed down problem but..
The program implements a wrapper around all of the OpenCL stuff. The wrapper detects all of the OpenCL devices then wraps these into yet another wrappers. The device wrappers contain all the objects related to it such as allocated cl_mem buffers the associated context etc.
I have chacked multiple times if there are no mistake, if no pointers are reused like if due to some error device wrappers from different platforms would share the same platform pointer. but no.
The problem:
When I divide work between all the computational devices on my laptop (CPU+Intel GPU + Nvidia GPU), kernel execution issued to the NVIDIA GPU crahses with CL_INVALID_COMMAND_QUEUE.
I've checked everything.
I've tried the following scenarios:
Intel GPU and CPU simultaneously => everything works fine
two CPUs simultaneously (server) => everything works fine
if I mix devices from two platforms on the laptop => it crashes with CL_INVALID_COMMAND_QUEUE. it crashes ONLY on the Nvidia GPU.
most of the initialization code is below.
std::cout << "Initializing the OpenCL engine..\n";
cl_int ret;
unsigned int nrOfActiveContexts = 0;
ret = clGetPlatformIDs(0, NULL, &mRetNumPlatforms);
if (mRetNumPlatforms > 0)
{
this->mPlatforms.resize(mRetNumPlatforms);
}
else
{
fprintf(stderr, "No OpenCL platform available.\n");
exit(1);
}
ret = clGetPlatformIDs(mRetNumPlatforms, mPlatforms.data(), NULL);
std::vector<cl_device_id> devices;
cl_context context;
cl_uint numberOfDevices;
//query for available compute platforms
for (int i = 0; i < mPlatforms.size() ; i++)
{
bool error = false;
numberOfDevices = 0;
devices.clear();
context = NULL;
cl_device_type deviceTypes = CL_DEVICE_TYPE_ALL;
if (useCPU &&useGPU)
deviceTypes = CL_DEVICE_TYPE_ALL;
else if (useCPU)
deviceTypes = CL_DEVICE_TYPE_CPU;
else if (useGPU)
deviceTypes = CL_DEVICE_TYPE_GPU;
ret = clGetDeviceIDs(mPlatforms[i], deviceTypes, 0, NULL, &numberOfDevices);
if (numberOfDevices > 0)
{
devices.resize( numberOfDevices);
ret = clGetDeviceIDs(mPlatforms[i], deviceTypes,
numberOfDevices, devices.data(), NULL);
}
else continue;
context = clCreateContext(NULL, numberOfDevices, devices.data(), NULL, NULL, &ret);
if (ret != CL_SUCCESS)
throw(std::abort);
mContexts.push_back(context);
if (ret != CL_SUCCESS)
{
error = true;
}
//query device properties create Workers
size_t ret_size;
cl_uint compute_units;
cl_ulong max_alloc;
size_t max_work_size;
std::string name;
std::vector<char> c_name;
for (int y = 0; y < devices.size(); y++)
{
ret_size = compute_units = max_alloc = max_work_size = 0;
c_name.clear();
ret = clGetDeviceInfo(devices[y], CL_DEVICE_NAME, NULL, NULL, &ret_size);
if (ret != CL_SUCCESS)
{
error = true; goto errored;
}
c_name.resize(ret_size);
ret = clGetDeviceInfo(devices[y], CL_DEVICE_NAME, c_name.size(), c_name.data(), &ret_size);
if (ret != CL_SUCCESS)
{
error = true; goto errored;
}
name = std::string(c_name.begin(), c_name.end());
name = std::regex_replace(name, std::regex("[' ']{2,}"), " ");
cl_device_type devType;
ret = clGetDeviceInfo(devices[y], CL_DEVICE_TYPE, sizeof(cl_device_type), (void *)&devType, NULL);
if (ret != CL_SUCCESS)
{
error = true; goto errored;
}
ret = clGetDeviceInfo(devices[y], CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(cl_uint), (void *)&compute_units, NULL);
if (ret != CL_SUCCESS)
{
error = true; goto errored;
}
ret = clGetDeviceInfo(devices[y], CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), (void *)&max_work_size, NULL);
if (ret != CL_SUCCESS)
{
error = true; goto errored;
}
CWorker::eWorkerType type;
if (devType & CL_DEVICE_TYPE_GPU)
type = CWorker::eWorkerType::GPU;
else
if (devType & CL_DEVICE_TYPE_CPU)
type = CWorker::eWorkerType::CPU;
if (type == CWorker::eWorkerType::CPU)
{
if (compute_units > 8)
max_work_size = compute_units / 4;
else if (compute_units == 8)
max_work_size = 2;
else
max_work_size = 1;
}
ret = clGetDeviceInfo(devices[y], CL_DEVICE_MAX_MEM_ALLOC_SIZE, sizeof(cl_ulong), (void *)&max_alloc, NULL);
if (ret != CL_SUCCESS)
{
error = true;
goto errored;
}
errored:
if (error != true)
{
CWorker * w = new CWorker();
w->setDevice(devices[y]);
w->setMaxComputeUnits(compute_units);
w->setMaxMemAlloc(max_alloc);
w->setMaxWorkGroupSize(max_work_size);
w->setName(name);
std::cmatch cm;
if (std::regex_search(name.data(), cm, std::regex("\\w\+")))
w->setShortName(std::string(cm[0]) +"-"+ std::to_string(mWorkers.size()+1));
w->setContext(context);
w->setType(type);
mWorkers.push_back(w);
}
}
nrOfActiveContexts++;
}
if (mWorkers.size() > 0)
mInitialised = true;
if (mWorkers.size() > 0)
return true;
else return false;
Quite likely the problem is with how the context is being created:
context = clCreateContext(NULL, numberOfDevices, devices.data(), NULL, NULL, &ret);
The first parameter being passed is NULL which according to the OpenCL manual means that the platform being selected is implementation defined:
Specifies a list of context property names and their corresponding values. Each property name is immediately followed by the corresponding desired value. The list is terminated with 0. properties can be NULL in which case the platform that is selected is implementation-defined.
Try passing something like this:
cl_context_properties properties[] = { CL_CONTEXT_PLATFORM, (cl_context_properties)(platforms[i])(), 0};
context = clCreateContext(properties, numberOfDevices, devices.data(), NULL, NULL, &ret);
If that doesn't help then maybe try to initialize Nvidia first (if that is not the case already). It may be that Intel is initialized first and it's OpenCL version driver is newer than Nvidia (for example Intel OpenCL 2.0 vs Nvidia 1.2) and some of that is being used for Nvidia hence error.

How to retrieve information from multiple/dual code signatures on an executable file

I've been using the following code (taken from KB323809 article) to retrieve information about the code signature on the executable file. This works fine for a single digital signature.
But how to retrieve information for multiple code signatures?
In that case the Microsoft code below simply retrives info only for the first signature.
My thought was to call CryptMsgGetParam with CMSG_SIGNER_COUNT_PARAM to get the number of signatures and then pass each signature index to the subsequent call to CryptMsgGetParam with CMSG_SIGNER_INFO_PARAM (in the code below.) But this approach always returns 1 signature, even if I clearly have more, like 3 in this example:
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "crypt32.lib")
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
typedef struct {
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info);
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st);
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext);
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,
PCMSG_SIGNER_INFO *pCounterSignerInfo);
int _tmain(int argc, TCHAR *argv[])
{
WCHAR szFileName[MAX_PATH];
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
BOOL fResult;
DWORD dwEncoding, dwContentType, dwFormatType;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
DWORD dwSignerInfo;
CERT_INFO CertInfo;
SPROG_PUBLISHERINFO ProgPubInfo;
SYSTEMTIME st;
ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
__try
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo <filename>\n"));
return 0;
}
#ifdef UNICODE
lstrcpynW(szFileName, argv[1], MAX_PATH);
#else
if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1)
{
printf("Unable to convert to unicode.\n");
__leave;
}
#endif
// Get message handle and store handle from the signed file.
fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
szFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
&dwEncoding,
&dwContentType,
&dwFormatType,
&hStore,
&hMsg,
NULL);
if (!fResult)
{
_tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
__leave;
}
// Get signer information size.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Allocate memory for signer information.
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
if (!pSignerInfo)
{
_tprintf(_T("Unable to allocate memory for Signer Info.\n"));
__leave;
}
// Get Signer Information.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Get program name and publisher information from
// signer info structure.
if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
{
if (ProgPubInfo.lpszProgramName != NULL)
{
wprintf(L"Program Name : %s\n",
ProgPubInfo.lpszProgramName);
}
if (ProgPubInfo.lpszPublisherLink != NULL)
{
wprintf(L"Publisher Link : %s\n",
ProgPubInfo.lpszPublisherLink);
}
if (ProgPubInfo.lpszMoreInfoLink != NULL)
{
wprintf(L"MoreInfo Link : %s\n",
ProgPubInfo.lpszMoreInfoLink);
}
}
_tprintf(_T("\n"));
// Search for the signer certificate in the temporary
// certificate store.
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print Signer certificate information.
_tprintf(_T("Signer Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Get the timestamp certificate signerinfo structure.
if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
{
// Search for Timestamp certificate in the temporary
// certificate store.
CertInfo.Issuer = pCounterSignerInfo->Issuer;
CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print timestamp certificate information.
_tprintf(_T("TimeStamp Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Find Date of timestamp.
if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
{
_tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
st.wMonth,
st.wDay,
st.wYear,
st.wHour,
st.wMinute);
}
_tprintf(_T("\n"));
}
}
__finally
{
// Clean up.
if (ProgPubInfo.lpszProgramName != NULL)
LocalFree(ProgPubInfo.lpszProgramName);
if (ProgPubInfo.lpszPublisherLink != NULL)
LocalFree(ProgPubInfo.lpszPublisherLink);
if (ProgPubInfo.lpszMoreInfoLink != NULL)
LocalFree(ProgPubInfo.lpszMoreInfoLink);
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return 0;
}
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext)
{
BOOL fReturn = FALSE;
LPTSTR szName = NULL;
DWORD dwData;
__try
{
// Print Serial Number.
_tprintf(_T("Serial Number: "));
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
for (DWORD n = 0; n < dwData; n++)
{
_tprintf(_T("%02x "),
pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);
}
_tprintf(_T("\n"));
// Get Issuer name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for Issuer name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for issuer name.\n"));
__leave;
}
// Get Issuer name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// print Issuer name.
_tprintf(_T("Issuer Name: %s\n"), szName);
LocalFree(szName);
szName = NULL;
// Get Subject name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for subject name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for subject name.\n"));
__leave;
}
// Get subject name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Print Subject Name.
_tprintf(_T("Subject Name: %s\n"), szName);
fReturn = TRUE;
}
__finally
{
if (szName != NULL) LocalFree(szName);
}
return fReturn;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
outputString = (LPWSTR)LocalAlloc(LPTR,
(wcslen(inputString) + 1) * sizeof(WCHAR));
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info)
{
BOOL fReturn = FALSE;
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
BOOL fResult;
__try
{
// Loop through authenticated attributes and find
// SPC_SP_OPUS_INFO_OBJID OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Get Size of SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for SPC_SP_OPUS_INFO structure.
OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData);
if (!OpusInfo)
{
_tprintf(_T("Unable to allocate memory for Publisher Info.\n"));
__leave;
}
// Decode and get SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Fill in Program Name if present.
if (OpusInfo->pwszProgramName)
{
Info->lpszProgramName =
AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
Info->lpszProgramName = NULL;
// Fill in Publisher Information if present.
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
Info->lpszPublisherLink = NULL;
break;
}
}
else
{
Info->lpszPublisherLink = NULL;
}
// Fill in More Info if present.
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
Info->lpszMoreInfoLink = NULL;
break;
}
}
else
{
Info->lpszMoreInfoLink = NULL;
}
fReturn = TRUE;
break; // Break from for loop.
} // lstrcmp SPC_SP_OPUS_INFO_OBJID
} // for
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
return fReturn;
}
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st)
{
BOOL fResult;
FILETIME lft, ft;
DWORD dwData;
BOOL fReturn = FALSE;
// Loop through authenticated attributes and find
// szOID_RSA_signingTime OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Decode and get FILETIME structure.
dwData = sizeof(ft);
fResult = CryptDecodeObject(ENCODING,
szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)&ft,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
break;
}
// Convert to local time.
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, st);
fReturn = TRUE;
break; // Break from for loop.
} //lstrcmp szOID_RSA_signingTime
} // for
return fReturn;
}
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
BOOL fReturn = FALSE;
BOOL fResult;
DWORD dwSize;
__try
{
*pCounterSignerInfo = NULL;
// Loop through unathenticated attributes for
// szOID_RSA_counterSign OID.
for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
{
if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId,
szOID_RSA_counterSign) == 0)
{
// Get size of CMSG_SIGNER_INFO structure.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for CMSG_SIGNER_INFO.
*pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize);
if (!*pCounterSignerInfo)
{
_tprintf(_T("Unable to allocate memory for timestamp info.\n"));
__leave;
}
// Decode and get CMSG_SIGNER_INFO structure
// for timestamp certificate.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)*pCounterSignerInfo,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
fReturn = TRUE;
break; // Break from for loop.
}
}
}
__finally
{
// Clean up.
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
}
return fReturn;
}
In addition to the answer of Daniel Sie.
Found an attribute (szOID_NESTED_SIGNATURE) that would contain the CMSG_SIGNER_INFO that need to be decoded with following steps (this is Delphi code but sense is clear):
LNestedMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, 0);
CryptMsgUpdate(LNestedMsg, LFindedAttr.rgValue.pbData, LFindedAttr.rgValue.cbData, True);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, LNestedSignerInfo, #LSize);
The acquired CMSG_SIGNER_INFO (LNestedSignerInfo) is the nested signature (in our case SHA2 signature).
To obtain the time-stamp information (RFC3161) of that signature - search the Unauthenticated attribute with pszObjId = szOID_RFC3161_counterSign (1.3.6.1.4.1.311.3.3.1).
Found attribute would contain the CMSG_SIGNER_INFO of the time-stamp counter signature, that also need to be decoded by previously described steps (CryptMsgOpenToDecode, CryptMsgUpdate, CryptMsgGetParam).
The CERT_CONTEXT of nested signature or timestamp counter signature is need to be searched in store with is obtained from corresponding HCRYPTMSG (result of CryptMsgOpenToDecode).
LNestedStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LNestedMsg);
LTimeStampStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LTimeStampMsg);
Example to decode szOID_RFC3161_counterSign attribute:
LNestedSignerAttr := LNestedSigner.UnauthAttrs.rgAttr;
for I := 0 to LNestedSigner.UnauthAttrs.cAttr - 1 do
begin
if SameText(string(LNestedSignerAttr.pszObjId), szOID_RFC3161_counterSign) then
begin
LNestedTimeStampMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, nil);
if not Assigned(LNestedTimeStampMsg) then
RaiseLastOSError;
try
if not CryptMsgUpdate(LNestedTimeStampMsg, LNestedSignerAttr.rgValue.pbData, LNestedSignerAttr.rgValue.cbData, True) then
RaiseLastOSError;
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize) then
RaiseLastOSError;
GetMem(LTimeStampSigner, LSize);
try
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, LTimeStampSigner, #LSize) then
RaiseLastOSError;
LAttr := LTimeStampSigner.AuthAttrs.rgAttr;
for J := 0 to LTimeStampSigner.AuthAttrs.cAttr - 1 do
begin
if SameText(string(LAttr.pszObjId), szOID_RSA_signingTime) then
begin
LSize := SizeOf(LFileTime);
if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
szOID_RSA_signingTime, LAttr.rgValue.pbData, LAttr.rgValue.cbData, 0, #LFileTime, #LSize) then
RaiseLastOSError;
if FileTimeToLocalFileTime(#LFileTime, LLocalFileTime)
and FileTimeToSystemTime(#LLocalFileTime, LSystemTime) then
SHA2TimeStamp := SystemTimeToDateTime(LSystemTime)
else
SHA2TimeStamp := 0;
end;
Inc(LAttr);
end;
finally
FreeMem(LTimeStampSigner);
end;
finally
if not CryptMsgClose(LNestedTimeStampMsg) then
RaiseLastOSError;
end;
end;
Inc(LNestedSignerAttr);
end;
Authenticode stores secondary signatures in the UnauthenticatedAttributes of primary signer (index 0), instead of additional PKCS 7 signer.
From the primary signature, search the UnauthenticatedAttribue for below:
//Indicates the attribute is an octet encoded PKCS7
define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"
The encoded object of this attribute is a full PKCS 7 signer.
Thanks.

ERROR_IO_PENDING on FTP Asynchronous Operation [Wininet C++]

I've recently been working on an application that needs to establish an FTP connection with a server and download/upload files from it. For performance reasons, I would like to download multiple files at a time. For that reason I've tried to implement asynchronous operation on the Wininet API using the InternetOpen function along with the INTERNET_FLAG_ASYNC flag, as well as the InternetSetStatusCallback function. Here is a sample of my code, where I want to list all the files in the main directory of the remote server recursively:
/*Global variables*/
HANDLE MayContinue=0;
DWORD LatestResult=1;
/*Prototypes*/
void CALLBACK CallbackFunction(HINTERNET,DWORD_PTR,DWORD,LPVOID,DWORD);
//Iteration function called by main()
void FTPIterate()
{
WIN32_FIND_DATAA data;
HINTERNET Internet;
INTERNET_STATUS_CALLBACK call;
HINTERNET h_file;
MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL);
iconnect=InternetOpen(NULL,INTERNET_OPEN_TYPE_PROXY,proxy_url,NULL,INTERNET_FLAG_ASYNC);
call=InternetSetStatusCallback(iconnect,(INTERNET_STATUS_CALLBACK)CallbackFunction);
while(f[FLAG_FTP_ITERATE])
{
MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL);
InternetConnect(iconnect,ftp_url,INTERNET_DEFAULT_FTP_PORT,ftp_user,ftp_pass,INTERNET_SERVICE_FTP,NULL,LatestResult);
WaitForSingleObject (MayContinue, INFINITE);
server=(HINTERNET)LatestResult;
printf("Server handle: %i\n",(int)server);
printf("Server Error: %i\n",GetLastError());
SetLastError(0);
MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL);
FtpFindFirstFile(server,ftp_base,&data,INTERNET_FLAG_NO_CACHE_WRITE,LatestResult);
WaitForSingleObject(MayContinue,INFINITE);
h_file=(HINTERNET)LatestResult;
//do stuff
printf("FindFirstFile handle: %i\n",(int)h_File);
while((MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL)) && InternetFindNextFileA(h_file,&data))
{
WaitForSingleObject(MayContinue,INFINITE);
//do stuff
}
printf("FindNextFile Error: %i\n",GetLastError()); //loop is never executed
InternetCloseHandle(server);
}
}
void CALLBACK CallbackFunction(HINTERNET hInternet,DWORD_PTR dwContext,DWORD dwInternetStatus,LPVOID lpvStatusInformation,DWORD dwStatusInformationLength)
{
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
{
LatestResult = ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult;
SetEvent (MayContinue);
}
}
My code is based on this post from Stack Overflow. When I run it, I first of all get an error after the call to InternetConnect, which is ERROR_IO_PENDING. According to the WinAPI reference this means that there's still some operation being performed. Shouldn't the call to WaitForSingleObject prevent this from happening? (actually, the HINTERNET handle returned by InternetConnect seems to be valid).
When I call the FtpFindFirstFile function it correctly retrieves the first file, but when I use the HINTERNET handle (which, again, seems to be valid) returned by it in the InternetFindNextFile function it fails with error INVALID_HANDLE_VALUE.
EDIT: I egt those errors when using Remy's code:
Connect Handle 00CC0004
Waiting for server handle
Unable to find first file. OS Error: 6 //aren't those calls to FindFirstFile weird if InternetConnect hasn't returned yet?
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle.
Unable to connect to Server. Inet Error: 12015 Waiting for server handle
Can someone help me find the mistake?
Thank you in advance.
The ERROR_IO_PENDING error is coming from InternetOpen() itself. Since WaitForSingleObject() is succeeding, it is not overwriting GetLastError() (it only does so on error, as most APIs do), so the error is being carried over from the result of InternetOpen(). This is not the correct way to use GetLastError(). Assume that all APIs overwrite GetLastError() (if documented to use GetLastError() at all), and make sure you call it immediately only if an API fails (unless documented as being used during success conditions).
What your code is doing is NOT asynchronous! You are issuing asynchronous API calls, but you are waiting for their results, which defeats the purpose. Your code is acting synchronously, the same as if you were to omit the INTERNAL_FLAG_ASYNC flag and WaitForSingleObject() calls (not to mention you are leaking event resource by calling CreateEvent() unnecessarily), eg:
void LogError(const char *Op)
{
DWORD err = GetLastError();
if (err == ERROR_INTERNET_EXTENDED_ERROR)
{
LPSTR szBuffer;
DWORD dwLength = 0;
InternetGetLastResponseInfoA(&err, NULL, &dwLength);
if (GetLastError() != INSUFFICIENT_BUFFER)
{
printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError());
return;
}
szBuffer = new char[dwLength+1];
InternetGetLastResponseInfoA(&err, szBuffer, &dwLength);
szBuffer[dwLength] = 0;
printf("%s. Inet Error: %u %s", Op, err, szBuffer);
delete[] szBuffer;
}
else
{
LPSTR lpBuffer = NULL;
DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL);
if (lpBuffer)
{
printf("%s. OS Error: %u %s", Op, err, lpBuffer);
LocalFree(lpBuffer);
}
else
printf("%s. OS Error: %u", Op, err);
}
printf("\n");
}
void FTPIterate()
{
WIN32_FIND_DATAA data;
HINTERNET hConnect;
HINTERNET hServer;
HINTERNET hFile;
hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, 0);
if (hConnect == NULL)
{
LogError("Unable to Open Internet");
return;
}
printf("Connect handle: %p\n", hConnect);
while (f[FLAG_FTP_ITERATE])
{
printf("Connecting to Server\n");
hServer = InternetConnect(hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, 0);
if (hServer == NULL)
{
LogError("Unable to connect to Server");
continue;
}
printf("Connected to Server. Server handle: %p\n", hServer);
printf("Finding first file\n");
hFile = FtpFindFirstFileA(hServer, ftp_base, &data, INTERNET_FLAG_NO_CACHE_WRITE, 0);
if (hFile == NULL)
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No files were found\n");
else
LogError("Unable to find first file");
}
else
{
printf("Find handle: %p\n", hFile);
do
{
//do stuff
printf("Finding next file\n");
if (!InternetFindNextFileA(hFile, &data))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No more files were found\n");
else
LogError("Unable to find next file")
break;
}
}
while (true);
InternetCloseHandle(hFile);
}
InternetCloseHandle(hServer);
}
InternetCloseHandle(hConnect);
}
To make this code run asynchronously, get rid of all off the waits and implement a state machine that your callback advances as needed, eg:
enum FTPState {ftpConnect, ftpWaitingForConnect, ftpFindFirstFile, ftpWaitingForFirstFind, ftpFindNextFile, ftpWaitingForNextFind, ftpProcessFile, ftpDisconnect};
struct REQ_CONTEXT
{
FTPState State;
WIN32_FIND_DATAA data;
HINTERNET hConnect;
HINTERNET hServer;
HINTERNET hFile;
HANDLE hDoneEvent;
};
void LogError(const char *Op, DWORD err)
{
if (err == ERROR_INTERNET_EXTENDED_ERROR)
{
LPSTR szBuffer;
DWORD dwLength = 0;
InternetGetLastResponseInfoA(&err, NULL, &dwLength);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError());
return;
}
szBuffer = new char[dwLength+1];
InternetGetLastResponseInfoA(&err, szBuffer, &dwLength);
szBuffer[dwLength] = 0;
printf("%s. Inet Error: %u %s", Op, err, szBuffer);
delete[] szBuffer;
}
else
{
LPSTR lpBuffer = NULL;
DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL);
if (lpBuffer)
{
printf("%s. OS Error: %u %s", Op, err, lpBuffer);
LocalFree(lpBuffer);
}
else
printf("%s. OS Error: %u", Op, err);
}
printf("\n");
}
void LogError(const char *Op)
{
LogError(Op, GetLastError());
}
void DoNextStep(REQ_CONTEXT *ctx)
{
do
{
if ((ctx->State == ftpConnect) && (!f[FLAG_FTP_ITERATE]))
{
printf("Done!\n");
SetEvent(ctx->hDoneEvent);
return;
}
switch (ctx->State)
{
case ftpConnect:
{
printf("Connecting to Server\n");
HINTERNET hServer = InternetConnect(ctx->hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, (DWORD_PTR)ctx);
if (hServer != NULL)
{
if (ctx->hServer == NULL)
{
ctx->hServer = hServer;
printf("Server handle: %p\n", ctx->hServer);
}
printf("Connected to Server\n");
ctx->State = ftpFindFirstFile;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
if (ctx->hServer == NULL)
printf("Waiting for Server handle\n");
printf("Waiting for Server connect to complete\n");
ctx->State = ftpWaitingForConnect;
}
else
LogError("Unable to connect to Server");
break;
}
case ftpWaitingForConnect:
return;
case ftpFindFirstFile:
{
printf("Finding first file\n");
HINTERNET hFile = FtpFindFirstFileA(ctx->hServer, ftp_base, &ctx->data, INTERNET_FLAG_NO_CACHE_WRITE, (DWORD_PTR)ctx);
if (hFile != NULL)
{
if (ctx->hFile == NULL)
{
ctx->hFile = hFile;
printf("Find handle: %p\n", ctx->hFile);
}
ctx->State = ftpProcessFile;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
if (ctx->hFile == NULL)
printf("Waiting for Find handle\n");
printf("Waiting for Find to complete\n");
ctx->State = ftpWaitingForFirstFind;
}
else
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No files were found\n");
else
LogError("Unable to find first file");
ctx->State = ftpDisconnect;
}
break;
}
case ftpWaitingForFirstFind:
case ftpWaitingForNextFind:
return;
case ftpProcessFile:
{
//do stuff
printf("Finding next file\n");
if (!InternetFindNextFileA(ctx->hFile, &ctx->data))
{
if (GetLastError() == ERROR_IO_PENDING)
{
printf("Waiting for next file to complete\n");
ctx->State = ftpWaitingForNextFind;
}
else
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No more files were found\n");
else
LogError("Unable to find next file");
ctx->State = ftpDisconnect;
}
}
break;
}
case ftpDisconnect:
{
printf("Disconnecting\n");
if (ctx->hFile != NULL)
{
InternetCloseHandle(ctx->hFile);
ctx->hFile = NULL;
}
if (ctx->hServer != NULL)
{
InternetCloseHandle(ctx->hServer);
ctx->hServer = NULL;
}
ctx->State = ftpConnect;
break;
}
}
}
while (true);
}
void CALLBACK CallbackFunction(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
REQ_CONTEXT *ctx = (REQ_CONTEXT*) dwContext;
switch (dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CREATED:
{
LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation;
switch (ctx->State)
{
case ftpConnect:
case ftpWaitingForConnect:
ctx->hServer = (HINTERNET) Result->dwResult;
printf("Server handle: %p\n", ctx->hServer);
break;
case ftpFindFirstFile:
case ftpWaitingForFirstFind:
ctx->hFile = (HINTERNET) Result->dwResult;
printf("Find handle: %p\n", ctx->hFile);
break;
}
break;
}
case INTERNET_STATUS_REQUEST_COMPLETE:
{
LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation;
switch (ctx->State)
{
case ftpWaitingForConnect:
{
if (!Result->dwResult)
{
LogError("Unable to connect to Server", Result->dwError);
ctx->State = ftpDisconnect;
}
else
{
printf("Connected to Server\n");
ctx->State = ftpFindFirstFile;
}
break;
}
case ftpWaitingForFirstFind:
case ftpWaitingForNextFind:
{
if (!Result->dwResult)
{
if (Result->dwError == ERROR_NO_MORE_FILES)
printf("No %sfiles were found\n", (ctx->State == ftpWaitingForNextFind) ? "more " : "");
else if (ctx->State == ftpWaitingForFirstFind)
LogError("Unable to find first file", Result->dwError);
else
LogError("Unable to find next file", Result->dwError);
ctx->State = ftpDisconnect;
}
else
ctx->State = ftpProcessFile;
break;
}
}
DoNextStep(ctx);
break;
}
}
}
void FTPIterate()
{
REQ_CONTEXT ctx = {0};
ctx.State = ftpConnect;
ctx.hDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (ctx.hDoneEvent == NULL)
{
LogError("Unable to Create Done Event");
return;
}
ctx.hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, INTERNET_FLAG_ASYNC);
if (ctx.hConnect == NULL)
{
LogError("Unable to Open Internet");
CloseHandle(ctx.hDoneEvent);
return;
}
printf("Connect handle: %p\n", ctx.hConnect);
InternetSetStatusCallback(ctx.hConnect, &CallbackFunction);
DoNextStep(&ctx);
WaitForSingleObject(ctx.hDoneEvent, INFINITE);
InternetCloseHandle(ctx.hConnect);
CloseHandle(ctx.hDoneEvent);
}

How to tell if Windows partition is active by a path on it?

My goal is to know if Windows is installed on an active disk partition. I can obtain the path for Windows:
C:\WINDOWS
and then it's partition:
\Device\Harddisk4\Partition4
But the question is how to know if this partition is active?
Check this Link (http://msdn.microsoft.com/en-us/library/windows/desktop/aa365451(v=vs.85).aspx)
PARTITION_INFORMATION has BootIndicator. but it is not guarantee about the running windows was booted by that partition.
Edited It is a example function tested on Windows7. I think 'activate' partition is not your goal. The 'activate' has meaning such as bootable USB device. I don't like WMI but it could be help your goal (http://msdn.microsoft.com/en-us/library/windows/desktop/bb986746(v=vs.85).aspx)
BOOL
__stdcall
TP_IsPartitionActivated(
__in LPCWSTR pPartition,
__out PBOOL pbIsActivated
)
{
HANDLE hDevice = INVALID_HANDLE_VALUE;
PARTITION_INFORMATION_EX szPartitionInformation;
DWORD cbReturned = 0x00;
if (pPartition == NULL || pbIsActivated == NULL) { return FALSE; }
__try
{
hDevice = CreateFileW(pPartition, 0x00, 0x00, NULL, OPEN_EXISTING, 0x00, NULL);
if (hDevice == INVALID_HANDLE_VALUE) { return FALSE; }
RtlZeroMemory(&szPartitionInformation, sizeof(szPartitionInformation));
if (FALSE != DeviceIoControl(hDevice, IOCTL_DISK_GET_PARTITION_INFO_EX, NULL, 0x00, (LPVOID)&szPartitionInformation, sizeof(PARTITION_INFORMATION_EX), &cbReturned, NULL))
{
if (PARTITION_STYLE_MBR == szPartitionInformation.PartitionStyle)
{
*pbIsActivated = szPartitionInformation.Mbr.BootIndicator;
}
else
{
}
return TRUE;
}
else
{
cbReturned = GetLastError();
wprintf(L"%08X(%d)\n", cbReturned, cbReturned);
}
}
__finally
{
if (hDevice != INVALID_HANDLE_VALUE) { CloseHandle(hDevice); }
}
return FALSE;
}
Call like
WCHAR szPartition[] = L"\\\\.\\C:";
BOOL bIsActivated = FALSE;
if (FALSE != TP_IsPartitionActivated(szPartition, &bIsActivated))
{
wprintf(L"%s \n", bIsActivated == FALSE ? L"not activated" : L"activated");
}
else
{
wprintf(L"function fail\n");
}