WinVerifyTrust function takes long time to excecute - signtool

I am using windows WinVerifyTrust function on windows 10 pro, to verify dll signatures.
when I activate this function for the first time, it takes 4 seconds for the function to execute and return verification status for the first dll. for the other proceeding dlls, the function returns at fast rate.
Can anyone help me understand the possible reason for that latency?
the call that takes 4 sec is this call:
lStatus = WinVerifyTrust(
NULL,
&WVTPolicyGUID,
&WinTrustData);
The wraper function I'm using looks like this:
#define _UNICODE 1
#define UNICODE 1
#include <tchar.h>
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <Softpub.h>
#include <wincrypt.h>
#include <wintrust.h>
// Link with the Wintrust.lib file.
#pragma comment (lib, "wintrust")
BOOL VerifyEmbeddedSignature(LPCWSTR pwszSourceFile)
{
LONG lStatus;
DWORD dwLastError;
// Initialize the WINTRUST_FILE_INFO structure.
WINTRUST_FILE_INFO FileData;
memset(&FileData, 0, sizeof(FileData));
FileData.cbStruct = sizeof(WINTRUST_FILE_INFO);
FileData.pcwszFilePath = pwszSourceFile;
FileData.hFile = NULL;
FileData.pgKnownSubject = NULL;
GUID WVTPolicyGUID = WINTRUST_ACTION_GENERIC_VERIFY_V2;
WINTRUST_DATA WinTrustData;
// Initialize the WinVerifyTrust input data structure.
// Default all fields to 0.
memset(&WinTrustData, 0, sizeof(WinTrustData));
WinTrustData.cbStruct = sizeof(WinTrustData);
// Use default code signing EKU.
WinTrustData.pPolicyCallbackData = NULL;
// No data to pass to SIP.
WinTrustData.pSIPClientData = NULL;
// Disable WVT UI.
WinTrustData.dwUIChoice = WTD_UI_NONE;
// No revocation checking.
WinTrustData.fdwRevocationChecks = WTD_REVOKE_NONE;
// Verify an embedded signature on a file.
WinTrustData.dwUnionChoice = WTD_CHOICE_FILE;
// Verify action.
WinTrustData.dwStateAction = WTD_STATEACTION_VERIFY;
// Verification sets this value.
WinTrustData.hWVTStateData = NULL;
// Not used.
WinTrustData.pwszURLReference = NULL;
// This is not applicable if there is no UI because it changes
// the UI to accommodate running applications instead of
// installing applications.
WinTrustData.dwUIContext = 0;
// Set pFile.
WinTrustData.pFile = &FileData;
// WinVerifyTrust verifies signatures as specified by the GUID
// and Wintrust_Data.
lStatus = WinVerifyTrust(
NULL,
&WVTPolicyGUID,
&WinTrustData);
switch (lStatus)
{
case ERROR_SUCCESS:
/*
Signed file:
- Hash that represents the subject is trusted.
- Trusted publisher without any verification errors.
- UI was disabled in dwUIChoice. No publisher or
time stamp chain errors.
- UI was enabled in dwUIChoice and the user clicked
"Yes" when asked to install and run the signed
subject.
*/
wprintf_s(L"The file \"%s\" is signed and the signature "
L"was verified.\n",
pwszSourceFile);
break;
case TRUST_E_NOSIGNATURE:
// The file was not signed or had a signature
// that was not valid.
// Get the reason for no signature.
dwLastError = GetLastError();
if (TRUST_E_NOSIGNATURE == dwLastError ||
TRUST_E_SUBJECT_FORM_UNKNOWN == dwLastError ||
TRUST_E_PROVIDER_UNKNOWN == dwLastError)
{
// The file was not signed.
wprintf_s(L"The file \"%s\" is not signed.\n",
pwszSourceFile);
}
else
{
// The signature was not valid or there was an error
// opening the file.
wprintf_s(L"An unknown error occurred trying to "
L"verify the signature of the \"%s\" file.\n",
pwszSourceFile);
}
break;
case TRUST_E_EXPLICIT_DISTRUST:
// The hash that represents the subject or the publisher
// is not allowed by the admin or user.
wprintf_s(L"The signature is present, but specifically "
L"disallowed.\n");
break;
case TRUST_E_SUBJECT_NOT_TRUSTED:
// The user clicked "No" when asked to install and run.
wprintf_s(L"The signature is present, but not "
L"trusted.\n");
break;
case CRYPT_E_SECURITY_SETTINGS:
wprintf_s(L"CRYPT_E_SECURITY_SETTINGS - The hash "
L"representing the subject or the publisher wasn't "
L"explicitly trusted by the admin and admin policy "
L"has disabled user trust. No signature, publisher "
L"or timestamp errors.\n");
break;
default:
wprintf_s(L"Error is: 0x%x.\n",
lStatus);
break;
}
// Any hWVTStateData must be released by a call with close.
WinTrustData.dwStateAction = WTD_STATEACTION_CLOSE;
lStatus = WinVerifyTrust(
NULL,
&WVTPolicyGUID,
&WinTrustData);
return true;
}

Please see MSDN documentation on WinVerifyTrust, it seems you will need to prevent retrieval of revocation lists as well:
// Use only the local cache for revocation checks. Prevents revocation checks over the network.
WinTrustData.dwProvFlags = WTD_CACHE_ONLY_URL_RETRIEVAL;

Related

How to get parameters of an Picture Transfer Protocol event in Windows Portable Devices?

I'm trying to support a digital camera with WPD. I have working request/data/response communication (PTP types 1,2,3), but have problems with events (type 4). From the event I need a command code (ex. 0xc102) and up to three integer parameters.
I have registered and am receiving events using the code from Microsoft's Portable Devices COM API Sample, and it catches the occurrence of the event easily - but I have no way to get the parameters. The command code is not provided directly, but it is embedded as part of the (supposedly random) CLSID.
I have tried to use the event object's IPortableDeviceValues as I would in parsing a response, but when trying to call GetIPortableDevicePropVariantCollectionValue the API returns a HRESULT of ERROR_NOT_FOUND (0x80070490). This call is used in getting params from the response object.
I tried (code inserted in CPortableDeviceEventsCallback::OnEvent):
HRESULT hr;
// Try to get all available data from the event:
DWORD pcelt{};
pEventParameters->GetCount(&pcelt);
printf("pEventParameters->GetCount: %d\n", pcelt);
for (unsigned int i = 0; i < pcelt; i++)
{
PROPERTYKEY pk;
PROPVARIANT pv;
pEventParameters->GetAt(i, &pk, &pv);
printf("PARAM %ws %d -> ", (PWSTR)CGuidToString(pk.fmtid), pk.pid);
switch (pv.vt)
{
case VT_UNKNOWN: printf("unknown\n"); break;
case VT_LPWSTR: printf("VT_LPWSTR: %ws\n", pv.pwszVal); break;
case VT_CLSID:
{
// note that OLECHAR is a typedef'd wchar_t
WCHAR szGUID[64] = { 0 };
_GUID guid = *pv.puuid;
(void)StringFromGUID2(guid, szGUID, 64);
printf("VT_CLSID: %ws\n", szGUID);
break;
}
default: printf("not supported vt %d\n", pv.vt); break;
}
}
// PRINTED: pEventParameters->GetCount: 3
// PRINTED: PARAM {15AB1953-F817-4FEF-A921-5676E838F6E0} 3 -> VT_CLSID: {C1020000-5738-4FF2-8445-BE3126691059}
// PRINTED: PARAM {4D545058-EF88-4E4D-95C3-4F327F728A96} 1011 -> VT_IUNKNOWN
// PRINTED: PARAM {15AB1953-F817-4FEF-A921-5676E838F6E0} 2 -> VT_LPWSTR: \\?\usb#vid_04da&pid_2382#0000000000000000000xhr1805180002#{6ac27878-a6fa-4155-ba85-f98f491d4f33}
// Try to get response code as if this was response (PTP type 3) - ERROR_NOT_SUPPORTED
DWORD dwResponseCode{};
hr = pEventParameters->GetUnsignedIntegerValue(WPD_PROPERTY_MTP_EXT_RESPONSE_CODE, &dwResponseCode);
assert(hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
// Try to get params as if this was response (PTP type 3) - ERROR_NOT_SUPPORTED
CComPtr<IPortableDevicePropVariantCollection> spRespParams;
hr = pEventParameters->GetIPortableDevicePropVariantCollectionValue(WPD_PROPERTY_MTP_EXT_RESPONSE_PARAMS, &spRespParams);
assert(hr == HRESULT_FROM_WIN32(ERROR_NOT_FOUND));
I also tried to get WPD_EVENT_PARAMETER_OBJECT_PARENT_PERSISTENT_UNIQUE_ID and WPD_EVENT_PARAMETER_OBJECT_CREATION_COOKIE from it, but they are not set.
There is also WPD_EVENT_ATTRIBUTE_OPTIONS, WPD_EVENT_ATTRIBUTE_NAME and WPD_EVENT_ATTRIBUTE_PARAMETERS - but I have no idea on how to get to them, nor is there anything of interest inside.
Or maybe what I seek is in the IUNKNOWN that I ignore in the switch case? But what type could it be?
The data is there, I can see it in Wireshark+USBPcap and other PTP applications use it.
Found the answer in https://blogs.msdn.microsoft.com/dimeby8/2006/10/06/listening-to-mtp-events/
It was the IUnknown, and it's type was IPortableDevicePropVariantCollection with GUID 4D545058-EF88-4E4D-95C3-4F327F728A96.

ESP32 SDCARD speed issue

I am trying to use SD Card on Wrover kit but it seems that speed is the big issue on the board itself.
At first I wanted to download file from the net and save it to SDCARD but it took too long so to test it I've written a loop to save some chars into file and create ~1MB large file on a SDCARD and it takes forever.
What could be the cause that ~1MB file could be such a long task to do.
I have combined two examples into one to do the task.
Also I have commented everything and left just part with writing to file to demonstrate issue.
/* ESP HTTP Client Example
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#define LOG_LOCAL_LEVEL ESP_LOG_ERROR
#include <string.h>
#include <stdlib.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_system.h"
#include "nvs_flash.h"
#include "app_wifi.h"
#include "esp_http_client.h"
/* SD CARD */
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"
#include "driver/sdspi_host.h"
#include "sdmmc_cmd.h"
/* SD CARD */
#define USE_SPI_MODE
#ifdef USE_SPI_MODE
// Pin mapping when using SPI mode.
// With this mapping, SD card can be used both in SPI and 1-line SD mode.
// Note that a pull-up on CS line is required in SD mode.
#define PIN_NUM_MISO 2
#define PIN_NUM_MOSI 15
#define PIN_NUM_CLK 14
#define PIN_NUM_CS 13
#endif //USE_SPI_MODE
//#define MAX_HTTP_RECV_BUFFER 512
#define MAX_HTTP_RECV_BUFFER 1024
static const char *TAG = "HTTP_CLIENT";
// ------------------ GLOBAL VARS -----------------------------
FILE *fp=NULL;
// ------------------ GLOBAL VARS -----------------------------
/* Root cert for howsmyssl.com, taken from howsmyssl_com_root_cert.pem
The PEM file was extracted from the output of this command:
openssl s_client -showcerts -connect www.howsmyssl.com:443 </dev/null
The CA root cert is the last cert given in the chain of certs.
To embed it in the app binary, the PEM file is named
in the component.mk COMPONENT_EMBED_TXTFILES variable.
*/
extern const char howsmyssl_com_root_cert_pem_start[] asm("_binary_howsmyssl_com_root_cert_pem_start");
extern const char howsmyssl_com_root_cert_pem_end[] asm("_binary_howsmyssl_com_root_cert_pem_end");
esp_err_t _http_event_handler(esp_http_client_event_t *evt)
{
switch(evt->event_id) {
case HTTP_EVENT_ERROR:
ESP_LOGE (TAG, "HTTP_EVENT_ERROR");
break;
case HTTP_EVENT_ON_CONNECTED:
ESP_LOGE (TAG, "HTTP_EVENT_ON_CONNECTED");
break;
case HTTP_EVENT_HEADER_SENT:
ESP_LOGE (TAG, "HTTP_EVENT_HEADER_SENT");
break;
case HTTP_EVENT_ON_HEADER:
ESP_LOGE (TAG, "HTTP_EVENT_ON_HEADER, key=%s, value=%s", evt->header_key, evt->header_value);
break;
case HTTP_EVENT_ON_DATA:
ESP_LOGE (TAG, "HTTP_EVENT_ON_DATA, len=%d", evt->data_len);
if (!esp_http_client_is_chunked_response(evt->client)) {
// Write out data
// printf("%.*s", evt->data_len, (char*)evt->data);
if(fp == NULL){
fp = fopen("/sdcard/muzika.mp3","wb");
}
if(fp != NULL){
fwrite(evt->data,1,evt->data_len,fp);
ESP_LOGE (TAG, "---------- HTTP_EVENT_ON_DATA - WRITING TO SD CARD ----------");
}
}
break;
case HTTP_EVENT_ON_FINISH:
ESP_LOGE (TAG, "HTTP_EVENT_ON_FINISH");
fclose(fp);
fp = NULL;
break;
case HTTP_EVENT_DISCONNECTED:
ESP_LOGE (TAG, "HTTP_EVENT_DISCONNECTED");
break;
}
return ESP_OK;
}
static void http_download_chunk()
{
esp_http_client_config_t config = {
//.url = "http://httpbin.org/stream-bytes/8912",
.url = "http://www.theoctopusproject.com/mp3/whatthey.mp3",
.event_handler = _http_event_handler,
};
esp_http_client_handle_t client = esp_http_client_init(&config);
esp_err_t err = esp_http_client_perform(client);
if (err == ESP_OK) {
ESP_LOGI(TAG, "HTTP chunk encoding Status = %d, content_length = %d",
esp_http_client_get_status_code(client),
esp_http_client_get_content_length(client));
} else {
ESP_LOGE(TAG, "Error perform http request %s", esp_err_to_name(err));
}
esp_http_client_cleanup(client);
}
static void http_test_task(void *pvParameters)
{
app_wifi_wait_connected();
ESP_LOGI(TAG, "Connected to AP, begin http example");
http_download_chunk();
ESP_LOGI(TAG, "Finish http example");
vTaskDelete(NULL);
}
void app_main()
{
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
// -- app_wifi_initialise();
/* SDCARD SETUP */
#ifndef USE_SPI_MODE
ESP_LOGI(TAG, "Using SDMMC peripheral");
sdmmc_host_t host = SDMMC_HOST_DEFAULT();
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
// To use 1-line SD mode, uncomment the following line:
// slot_config.width = 1;
// GPIOs 15, 2, 4, 12, 13 should have external 10k pull-ups.
// Internal pull-ups are not sufficient. However, enabling internal pull-ups
// does make a difference some boards, so we do that here.
gpio_set_pull_mode(15, GPIO_PULLUP_ONLY); // CMD, needed in 4- and 1- line modes
gpio_set_pull_mode(2, GPIO_PULLUP_ONLY); // D0, needed in 4- and 1-line modes
gpio_set_pull_mode(4, GPIO_PULLUP_ONLY); // D1, needed in 4-line mode only
gpio_set_pull_mode(12, GPIO_PULLUP_ONLY); // D2, needed in 4-line mode only
gpio_set_pull_mode(13, GPIO_PULLUP_ONLY); // D3, needed in 4- and 1-line modes
#else
ESP_LOGI(TAG, "Using SPI peripheral");
sdmmc_host_t host = SDSPI_HOST_DEFAULT();
sdspi_slot_config_t slot_config = SDSPI_SLOT_CONFIG_DEFAULT();
slot_config.gpio_miso = PIN_NUM_MISO;
slot_config.gpio_mosi = PIN_NUM_MOSI;
slot_config.gpio_sck = PIN_NUM_CLK;
slot_config.gpio_cs = PIN_NUM_CS;
// This initializes the slot without card detect (CD) and write protect (WP) signals.
// Modify slot_config.gpio_cd and slot_config.gpio_wp if your board has these signals.
#endif //USE_SPI_MODE
// Options for mounting the filesystem.
// If format_if_mount_failed is set to true, SD card will be partitioned and
// formatted in case when mounting fails.
esp_vfs_fat_sdmmc_mount_config_t mount_config = {
.format_if_mount_failed = false,
.max_files = 5,
.allocation_unit_size = 16 * 1024
};
// Use settings defined above to initialize SD card and mount FAT filesystem.
// Note: esp_vfs_fat_sdmmc_mount is an all-in-one convenience function.
// Please check its source code and implement error recovery when developing
// production applications.
sdmmc_card_t* card;
ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
if (ret != ESP_OK) {
if (ret == ESP_FAIL) {
ESP_LOGE(TAG, "Failed to mount filesystem. "
"If you want the card to be formatted, set format_if_mount_failed = true.");
} else {
ESP_LOGE(TAG, "Failed to initialize the card (%s). "
"Make sure SD card lines have pull-up resistors in place.", esp_err_to_name(ret));
}
return;
}
// Card has been initialized, print its properties
sdmmc_card_print_info(stdout, card);
// Use POSIX and C standard library functions to work with files.
// First create a file.
ESP_LOGE(TAG, "Opening file");
FILE* f = fopen("/sdcard/hello.txt", "w");
if (f == NULL) {
ESP_LOGE(TAG, "Failed to open file for writing");
return;
}
// ------------------------ WRITE INTO FILE -------------------
int j=0;
for(int i = 0 ; i < 1048576 ; i++,j++){
//fprintf(f, "Hello %s!\n", card->cid.name);
fprintf(f, "X");
if(j > 1024){
vTaskDelay(3);
j = 0;
}
}
fclose(f);
ESP_LOGE(TAG, "File written");
// ------------------------ WRITE INTO FILE -------------------
//-- xTaskCreate(&http_test_task, "http_test_task", 8192, NULL, 5, NULL);
//xTaskCreate(&http_test_task, "http_test_task", 16384, NULL, 5, NULL);
}
Your example is writing one byte at a time. That won't get you an accurate idea of how the overall speed of writing to the SD card - your code will spend a disproportionate amount of time processing the individual writes.
If you want to get a more accurate idea of how fast you can write to the card, try writing 1024 bytes (or even more) at a time. Benchmarks that measure maximum throughput and performance-sensitive applications will always write as much data as they can at a time in order to minimize the overhead of the write.
Also, don't use fprintf() if possible. It has extra overhead because it parses the format string. Try fwrite() - it will have less overhead and will give you a better idea of what you can expect in the best case.
Try something like this instead:
char *buf = malloc(1024);
if(buf) {
for(int i = 0; i < 1024; i++) {
fwrite(buf, 1, 1024, f);
vTaskDelay(3); // you'll speed this up if you can omit this
}
ESP_LOGE(TAG, "File written");
free(buf);
} else
ESP_LOGE(TAG, "malloc() failed");
fclose(f);

Signing an appxbundle using CryptUIWizDigitalSign API

I'm facing a rather interesting issue in regards to Authenticode signing an UWP appxbundle file.
Some background:
The client provided us with a SafeNet USB token containing the signing certificate. The private key is not exportable, of course. I want to be able to use this certificate for our automated release builds to sign the package. Unfortunately, the token requires a PIN to be entered once per session, so for example if the build agent reboots, the build will fail. We enabled single login on the token so it's enough to unlock it once a session.
Current state:
We can use signtool on the appxbundle without any problems, given the token has been unlocked. This works well enough but breaks as soon as the machine is rebooted or the workstation is locked.
After some searching I managed to find this piece of code. This takes the signing parameters (including the token PIN) and invokes Windows API to sign the target file. I managed to compile this and it worked flawlessly for signing the installation wrapper (EXE file) - the token did not ask for PIN and was unlocked automatically by the API call.
However, when I invoked the same code on the appxbundle file, the call to CryptUIWizDigitalSign failed with error code 0x80080209 APPX_E_INVALID_SIP_CLIENT_DATA. This is a mystery to me because invoking signtool on the same bundle, with the same parameters/certificate works without problem so the certificate should be fully compatible with the package.
Does anyone have experience with something like this? Is there a way to figure out what is the root cause of the error (what is incompatible between my cert and the bundle)?
EDIT 1
In response to a comment:
The code I'm using to call the APIs (taken directly from the aforementioned SO question)
#include <windows.h>
#include <cryptuiapi.h>
#include <iostream>
#include <string>
#pragma comment (lib, "cryptui.lib")
const std::wstring ETOKEN_BASE_CRYPT_PROV_NAME = L"eToken Base Cryptographic Provider";
std::string utf16_to_utf8(const std::wstring& str)
{
if (str.empty())
{
return "";
}
auto utf8len = ::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), NULL, 0, NULL, NULL);
if (utf8len == 0)
{
return "";
}
std::string utf8Str;
utf8Str.resize(utf8len);
::WideCharToMultiByte(CP_UTF8, 0, str.data(), str.size(), &utf8Str[0], utf8Str.size(), NULL, NULL);
return utf8Str;
}
struct CryptProvHandle
{
HCRYPTPROV Handle = NULL;
CryptProvHandle(HCRYPTPROV handle = NULL) : Handle(handle) {}
~CryptProvHandle() { if (Handle) ::CryptReleaseContext(Handle, 0); }
};
HCRYPTPROV token_logon(const std::wstring& containerName, const std::string& tokenPin)
{
CryptProvHandle cryptProv;
if (!::CryptAcquireContext(&cryptProv.Handle, containerName.c_str(), ETOKEN_BASE_CRYPT_PROV_NAME.c_str(), PROV_RSA_FULL, CRYPT_SILENT))
{
std::wcerr << L"CryptAcquireContext failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
if (!::CryptSetProvParam(cryptProv.Handle, PP_SIGNATURE_PIN, reinterpret_cast<const BYTE*>(tokenPin.c_str()), 0))
{
std::wcerr << L"CryptSetProvParam failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return NULL;
}
auto result = cryptProv.Handle;
cryptProv.Handle = NULL;
return result;
}
int wmain(int argc, wchar_t** argv)
{
if (argc < 6)
{
std::wcerr << L"usage: etokensign.exe <certificate file path> <private key container name> <token PIN> <timestamp URL> <path to file to sign>\n";
return 1;
}
const std::wstring certFile = argv[1];
const std::wstring containerName = argv[2];
const std::wstring tokenPin = argv[3];
const std::wstring timestampUrl = argv[4];
const std::wstring fileToSign = argv[5];
CryptProvHandle cryptProv = token_logon(containerName, utf16_to_utf8(tokenPin));
if (!cryptProv.Handle)
{
return 1;
}
CRYPTUI_WIZ_DIGITAL_SIGN_EXTENDED_INFO extInfo = {};
extInfo.dwSize = sizeof(extInfo);
extInfo.pszHashAlg = szOID_NIST_sha256; // Use SHA256 instead of default SHA1
CRYPT_KEY_PROV_INFO keyProvInfo = {};
keyProvInfo.pwszContainerName = const_cast<wchar_t*>(containerName.c_str());
keyProvInfo.pwszProvName = const_cast<wchar_t*>(ETOKEN_BASE_CRYPT_PROV_NAME.c_str());
keyProvInfo.dwProvType = PROV_RSA_FULL;
CRYPTUI_WIZ_DIGITAL_SIGN_CERT_PVK_INFO pvkInfo = {};
pvkInfo.dwSize = sizeof(pvkInfo);
pvkInfo.pwszSigningCertFileName = const_cast<wchar_t*>(certFile.c_str());
pvkInfo.dwPvkChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK_PROV;
pvkInfo.pPvkProvInfo = &keyProvInfo;
CRYPTUI_WIZ_DIGITAL_SIGN_INFO signInfo = {};
signInfo.dwSize = sizeof(signInfo);
signInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE;
signInfo.pwszFileName = fileToSign.c_str();
signInfo.dwSigningCertChoice = CRYPTUI_WIZ_DIGITAL_SIGN_PVK;
signInfo.pSigningCertPvkInfo = &pvkInfo;
signInfo.pwszTimestampURL = timestampUrl.c_str();
signInfo.pSignExtInfo = &extInfo;
if (!::CryptUIWizDigitalSign(CRYPTUI_WIZ_NO_UI, NULL, NULL, &signInfo, NULL))
{
std::wcerr << L"CryptUIWizDigitalSign failed, error " << std::hex << std::showbase << ::GetLastError() << L"\n";
return 1;
}
std::wcout << L"Successfully signed " << fileToSign << L"\n";
return 0;
}
The certificate is a CER file (public portion only) exported from the token and the container name is taken from the token's info. As I mentioned, this works correctly for EXE files.
The signtool command
signtool sign /sha1 "cert thumbprint" /fd SHA256 /n "subject name" /t "http://timestamp.verisign.com/scripts/timestamp.dll" /debug "$path"
This also works, when I call it either manually or from the CI build when the token is unlocked. But the code above fails with the mentioned error.
EDIT 2
Thanks to all of you, I now have a working implementation! I ended up using the SignerSignEx2 API, as suggested by RbMm. This seems to work fine for both appx bundles and PE files (different parameters for each). Verified on Windows 10 with a TFS 2017 build agent - unlocks the token, finds a specified certificate in the cert store, and signs+timestamps the specified file.
I published the result on GitHub, if anyone is interested: https://github.com/mareklinka/SafeNetTokenSigner
first of all i look where CryptUIWizDigitalSign failed:
the CryptUIWizDigitalSign called SignerSignEx function, with pSipData == 0. for sign PE file (exe, dll, sys) - this is ok and will be work. but for appxbundle (zip archive file type) this parameter mandatory and must point to APPX_SIP_CLIENT_DATA: for appxbundle call stack is
CryptUIWizDigitalSign
SignerSignEx
HRESULT Appx::Packaging::AppxSipClientData::Initialize(SIP_SUBJECTINFO* subjectInfo)
at very begin of Appx::Packaging::AppxSipClientData::Initialize we can view next code:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
this is exactly where your code fail.
instead of CryptUIWizDigitalSign need direct call SignerSignEx2 and pSipData is mandatory parameter in this case.
in msdn exist full worked example - How to programmatically sign an app package (C++)
the key point here:
APPX_SIP_CLIENT_DATA sipClientData = {};
sipClientData.pSignerParams = &signerParams;
signerParams.pSipData = &sipClientData;
the modern SignTool call SignerSignEx2 direct:
here again clear visible:
if (!subjectInfo->pClientData) return APPX_E_INVALID_SIP_CLIENT_DATA;
after this called
HRESULT Appx::Packaging::Packaging::SignFile(
PCWSTR FileName, APPX_SIP_CLIENT_DATA* sipClientData)
here at begin next code:
if (!sipClientData->pSignerParams) return APPX_E_INVALID_SIP_CLIENT_DATA;
this clear stated in msdn:
You must provide a pointer to an APPX_SIP_CLIENT_DATA structure as
the pSipData parameter when you sign an app package. You must
populate the pSignerParams member of APPX_SIP_CLIENT_DATA with
the same parameters that you use to sign the app package. To do this,
define your desired parameters on the SIGNER_SIGN_EX2_PARAMS
structure, assign the address of this structure to pSignerParams,
and then directly reference the structure's members as well when you
call SignerSignEx2.
question - why need again provide the same parameters, which used in call SignerSignEx2 ? because appxbundle is really archive, which containing multiple files. and every file need be sign. for this Appx::Packaging::Packaging::SignFile recursive call SignerSignEx2 again :
for this recursive calls pSignerParams and used - for call SignerSignEx2 with exactly the same parameters as top call

NetUSerSetInfo does not set UF_PASSWORD_EXPIRED

I'm trying to write an application that is supposed to force a user to change his/her password at next logon. So I have tried to use NetUserSetInfo with structures 1001, 1008, or 1017, on flag UF_PASSWORD_EXPIRED (for 1001 and 1008), and the current epoch (for 1017). I also tried to combine 1008 and 1017. Knowing that I have tested my application from the Administrator account.
I also tried to call NetUserSetInfo with the computer name NULL, "." and "MyComputer" (I found a post saying that it works with). With NULL the return error code is 0. With "." it is ERROR_IO_PENDING, and with"MyComputer"it is "invalid name".
But all the experiments give the same result. I.e. the flag is not set.
I found a post saying that the flag UF_PASSWORD_EXPIRED is for XP only. But it is not compliant with the Windows documentation. And this flag is set if I check "user must change password at next logon" from the control panel.
So how to set this flag programmatically?
Thanks
Unfortunately, the documentation for the Network Management API does not always clearly state whether the state of any particular flag can be changed or is read-only, but I've done some testing, and it looks as if the UF_PASSWORD_EXPIRED flag is one of the read-only ones.
The correct way to force a password reset is using usri4_password_expired,
When you call NetUserAdd or NetUserSetInfo, specify a nonzero value in this member to inform users that they must change their password at the next logon.
This worked for me:
#include <Windows.h>
#include <LM.h>
#pragma comment(lib, "netapi32.lib")
#include <stdio.h>
int wmain(int argc, wchar_t ** argv)
{
// argv[1] must contain the username
DWORD err;
USER_INFO_4 *ui4;
err = NetUserGetInfo(NULL, argv[1], 4, (LPBYTE*)(&ui4));
if (err != 0)
{
printf("NetUserGetInfo: %u\n", err);
return 1;
}
ui4->usri4_password_expired = 1;
err = NetUserSetInfo(NULL, argv[1], 4, (LPBYTE)ui4, NULL);
if (err != 0)
{
printf("NetUserSetInfo: %u\n", err);
return 1;
}
NetApiBufferFree(ui4);
printf("OK\n");
return 0;
}

DeviceIoControl error 1 incorrect function

I have created a device in kernel space and the access it in user space using CreateFile I am able to send ioctl to the driver and they are executed properly. The don't know how to trace what happens after WdfRequestComplete and upon return I end with error 1 (invalid function). Before this is flagged as dup please note there is a difference with this in that I write my driver ioctl and in that I am using synch io not asynch.
In user space:
fd = CreateFile(dev_path,
(FILE_GENERIC_READ | FILE_GENERIC_WRITE),
(FILE_SHARE_READ | FILE_SHARE_WRITE),
NULL, OPEN_EXISTING, 0, NULL);
// ... error checking code here
DeviceIoControl(fd, // device handler
VIRTQC_CMD_MMAP, // command to send
&inputBuffer,
inputBufferLength,
&outputBuffer,
outputBufferLength,
&returnLength,
(LPOVERLAPPED)NULL); // overlapped structure not needed using sync io
and in Kernel space
status = WdfRequestRetrieveInputBuffer(Request, InputBufferLength, &inputBuffer, NULL);
if (!NT_SUCCESS(status))
{
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
inputVirtArg = (VirtioQCArg*)inputBuffer;
status = WdfRequestRetrieveOutputBuffer(Request, OutputBufferLength, &outputBuffer, NULL);
if (!NT_SUCCESS(status))
{
WdfRequestComplete(Request, STATUS_INVALID_PARAMETER);
return;
}
outputVirtArg = (VirtioQCArg*)outputBuffer;
switch (IoControlCode)
{
case VIRTQC_CMD_MMAP:
if (PsGetCurrentThread() == irp->Tail.Overlay.Thread)
{
status = CreateAndMapMemory(device, &(inputVirtArg), &(outputVirtArg));
outputVirtArg->flag = (!NT_SUCCESS(status)) ? 0 : 1;
}
else
status = STATUS_UNSUCCESSFUL;
break;
default:
status = STATUS_INVALID_DEVICE_REQUEST;
break;
}
WdfRequestComplete(Request, status);
Update 1:
I have tried WdfRequestCompleteWithInformation(Request, status, OutputBufferLength); but same result.
Also, I notice that the address of inputBuffer and outputBuffer are the same.
Update 2:
I tried doing
temp = ExAllocatePoolWithTag(
NonPagedPool,
PAGE_SIZE,
MEMORY_TAG
);
// other code to
RtlCopyMemory((VirtioQCArg*)outputBuffer, temp, OutputBufferLength);
still get error 1
I had defined my ioctl cmds as enums in my linux driver (which works fine) and when implementing the driver in windows I used the same enum definition.
enum
{
// Module & Execution control (driver API)
VIRTIQC_SET = 200,
VIRTIQC_UNSET,
// more cmds.....
}
In windows defining control codes take a bit more. As explained here the CTL_CODE macro should be used to define new IOCTL control codes.
#define IOCTL_Device_Function CTL_CODE(DeviceType, Function, Method, Access)
In my case I ended up with defining this:
#define VIRTQC_MAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC8, METHOD_IN_DIRECT, FILE_READ_DATA | FILE_WRITE_DATA)
#define VIRTQC_UNMAP CTL_CODE(FILE_DEVICE_NETWORK, 0xC9, METHOD_OUT_DIRECT, FILE_READ_DATA)
I know the function code less than 0x800 are reserved for MS but the device on my host requires this codes so I'm just providing what is being asked.

Resources