Integrate Google Crashpad with Linux application - c++

I'm trying to integrate Google's Crashpad into my application running on Ubuntu.
As per it's overview design
I create one handler process on ubuntu by following this link
Now for the client process, I should register it with the handler via a socket connection.
Linux/Android
On Linux, a registration is a connected socket pair between a client process and the Crashpad handler. This socket pair may be private or shared among many client processes.
How do I do that?
There is not much information available on internet related to crashpad. Can someone provide link to any working example

Unless you have a special use case that isn't listed here you shouldn't have to do anything with sockets manually. Just create a new instance of CrashpadClient at the entry point of your program and call StartHandler.
Here's a snippet from BugSplat's myUbuntuCrasher sample:
// Start crash handler
CrashpadClient *client = new CrashpadClient();
bool status = client->StartHandler(handler, reportsDir, metricsDir, url, annotations, arguments, true, false, attachments);
Here's the full example from main.cpp:
#include <stdio.h>
#include <unistd.h>
#include "client/crashpad_client.h"
#include "client/crash_report_database.h"
#include "client/settings.h"
#define MIN(x, y) (((x) < (y)) ? (x) : (y))
#if defined(OS_POSIX)
typedef std::string StringType;
#elif defined(OS_WIN)
typedef std::wstring StringType;
#endif
using namespace base;
using namespace crashpad;
using namespace std;
bool initializeCrashpad(void);
StringType getExecutableDir(void);
void crash(void);
int main(int argc, char **argv) {
initializeCrashpad();
crash();
}
void crash() {
*(volatile int *)0 = 0;
}
bool initializeCrashpad() {
// Get directory where the exe lives so we can pass a full path to handler, reportsDir and metricsDir
StringType exeDir = getExecutableDir();
// Ensure that handler is shipped with your application
FilePath handler(exeDir + "/../crashpad/bin/crashpad_handler");
// Directory where reports will be saved. Important! Must be writable or crashpad_handler will crash.
FilePath reportsDir(exeDir);
// Directory where metrics will be saved. Important! Must be writable or crashpad_handler will crash.
FilePath metricsDir(exeDir);
// Configure url with BugSplat’s public fred database. Replace 'fred' with the name of your BugSplat database.
StringType url = "http://fred.bugsplat.com/post/bp/crash/crashpad.php";
// Metadata that will be posted to the server with the crash report map
map<StringType, StringType> annotations;
annotations["format"] = "minidump"; // Required: Crashpad setting to save crash as a minidump
annotations["database"] = "fred"; // Required: BugSplat database
annotations["product"] = "myUbuntuCrasher"; // Required: BugSplat appName
annotations["version"] = "1.0.0"; // Required: BugSplat appVersion
annotations["key"] = "Sample key"; // Optional: BugSplat key field
annotations["user"] = "fred#bugsplat.com"; // Optional: BugSplat user email
annotations["list_annotations"] = "Sample comment"; // Optional: BugSplat crash description
// Disable crashpad rate limiting so that all crashes have dmp files
vector<StringType> arguments;
arguments.push_back("--no-rate-limit");
// File paths of attachments to be uploaded with the minidump file at crash time - default bundle limit is 2MB
vector<FilePath> attachments;
FilePath attachment(exeDir + "/attachment.txt");
attachments.push_back(attachment);
// Initialize Crashpad database
unique_ptr<CrashReportDatabase> database = CrashReportDatabase::Initialize(reportsDir);
if (database == NULL) return false;
// Enable automated crash uploads
Settings *settings = database->GetSettings();
if (settings == NULL) return false;
settings->SetUploadsEnabled(true);
// Start crash handler
CrashpadClient *client = new CrashpadClient();
bool status = client->StartHandler(handler, reportsDir, metricsDir, url, annotations, arguments, true, false, attachments);
return status;
}
StringType getExecutableDir() {
char pBuf[FILENAME_MAX];
int len = sizeof(pBuf);
int bytes = MIN(readlink("/proc/self/exe", pBuf, len), len - 1);
if (bytes >= 0) {
pBuf[bytes] = '\0';
}
char* lastForwardSlash = strrchr(&pBuf[0], '/');
if (lastForwardSlash == NULL) return NULL;
*lastForwardSlash = '\0';
return pBuf;
}
More information about configuring Crashpad in Ubuntu can be found here.

Related

ETW - Realtime consuming of event trace

I'm not a C++ developer so apologies for any imprecise language.
I have a ETW Kernel Logger configured (basically a tweaked version of the Microsoft examples). It writes events to a log and I can view the data from the etl file. I would like to switch LogFileMode to EVENT_TRACE_REAL_TIME_MODE and interact with the data as it passes through the trace.
A specific example would be something like
foreach ($event in $trace) {
if ($string in $event) {
print $event
}
}
The documentation I've read suggests I need a consumer which would run the OpenTrace function, process the events with a callback, and then close the trace? Unfortunately, I have not seen an example of a consumer like this which I can understand. Is it possible take the msft example code below and modify it to do what I'm describing or is this not a possible approach?
#define INITGUID
#define UNICODE 1
#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <strsafe.h>
#include <wmistr.h>
#include <evntrace.h>
#define LOGFILE_PATH L"C:\\Users\\userplace\\testtrace.etl"
int main(void)
{
ULONG status = ERROR_SUCCESS;
TRACEHANDLE SessionHandle = 0;
EVENT_TRACE_PROPERTIES* pSessionProperties = NULL;
ULONG BufferSize = 0;
// Allocate memory for the session properties. The memory must
// be large enough to include the log file name and session name,
// which get appended to the end of the session properties structure.
BufferSize = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(LOGFILE_PATH) + sizeof(KERNEL_LOGGER_NAME);
pSessionProperties = (EVENT_TRACE_PROPERTIES*) malloc(BufferSize);
if (NULL == pSessionProperties)
{
wprintf(L"Unable to allocate %d bytes for properties structure.\n", BufferSize);
goto cleanup;
}
ZeroMemory(pSessionProperties, BufferSize);
pSessionProperties->Wnode.BufferSize = BufferSize;
pSessionProperties->Wnode.Flags = WNODE_FLAG_TRACED_GUID;
pSessionProperties->Wnode.ClientContext = 1; //QPC clock resolution
pSessionProperties->Wnode.Guid = SystemTraceControlGuid;
pSessionProperties->EnableFlags = EVENT_TRACE_FLAG_FILE_IO_INIT | EVENT_TRACE_FLAG_PROCESS;
pSessionProperties->LogFileMode = EVENT_TRACE_FILE_MODE_CIRCULAR;
pSessionProperties->MaximumFileSize = 5; // 5 MB
pSessionProperties->LoggerNameOffset = sizeof(EVENT_TRACE_PROPERTIES);
pSessionProperties->LogFileNameOffset = sizeof(EVENT_TRACE_PROPERTIES) + sizeof(KERNEL_LOGGER_NAME);
StringCbCopy((LPWSTR)((char*)pSessionProperties + pSessionProperties->LogFileNameOffset), sizeof(LOGFILE_PATH), LOGFILE_PATH);
// Create the trace session.
status = StartTrace((PTRACEHANDLE)&SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties);
if (ERROR_SUCCESS != status)
{
if (ERROR_ALREADY_EXISTS == status)
{
wprintf(L"The NT Kernel Logger session is already in use.\n");
}
else
{
wprintf(L"EnableTrace() failed with %lu\n", status);
}
goto cleanup;
}
wprintf(L"Press any key to end trace session ");
_getch();
cleanup:
if (SessionHandle)
{
status = ControlTrace(SessionHandle, KERNEL_LOGGER_NAME, pSessionProperties, EVENT_TRACE_CONTROL_STOP);
if (ERROR_SUCCESS != status)
{
wprintf(L"ControlTrace(stop) failed with %lu\n", status);
}
}
if (pSessionProperties)
free(pSessionProperties);
}
Here is the minimalistic example based on MSDN:
void create_realtime_consumer(const wchar_t * session_name, EVENT_RECORD_CALLBACK * event_callback, EVENT_TRACE_BUFFER_CALLBACKW * buffer_callback)
{
EVENT_TRACE_LOGFILE trace{};
TRACE_LOGFILE_HEADER * pHeader = &trace.LogfileHeader;
TDHSTATUS status = ERROR_SUCCESS;
trace.LoggerName = (LPWSTR)session_name; // use KERNEL_LOGGER_NAMEW to consume Kernel events
trace.ProcessTraceMode = PROCESS_TRACE_MODE_REAL_TIME | PROCESS_TRACE_MODE_RAW_TIMESTAMP | PROCESS_TRACE_MODE_EVENT_RECORD; // create real time sesion + event should be represented as EVENT_RECORD structure
trace.EventRecordCallback = event_callback; // called on each event
trace.BufferCallback = buffer_callback; // called on each ETWbuffer flush
auto h_trace = OpenTrace(&trace);
if(h_trace == INVALID_PROCESSTRACE_HANDLE)
throw std::runtime_error("Unable to open trace");
if(pHeader->PointerSize != sizeof(PVOID))
pHeader = (PTRACE_LOGFILE_HEADER)((PUCHAR)pHeader + 2 * (pHeader->PointerSize - sizeof(PVOID)));
try {
status = ProcessTrace(&h_trace, 1, 0, 0); // this call blocks until either the session is stopped or an exception is occurred in event_callback
}
catch(...) {
// catch exceptions occurred in event_callback
}
CloseTrace(h_trace);
}
This code runs the consumer to process Kernel events in real time.
To make it work you should first create ETW session (like in the example you mentioned above but you need to specify EVENT_TRACE_REAL_TIME_MODE in LogFileMode) and then run the consumer.
You should pass KERNEL_LOGGER_NAMEW as session_name to consume Kernel events.

Calling a Constructor in a custom .NET Core Host

I am trying to write a .NET Core host using coreclr.h. To do this I am trying to create function pointers to the c# code. I am able to call the static methods from my host, but calling the methods that depend on an object directly are not able to be called, ideally I would like to be able to call the constructor and all non-static methods from the C++ without modifying the C#. I can call Multiply5 and Main fine, but there is a segfault when the Program constructor or Add is called, is there any way to fix this? This is a Linux system so C++/CLI is not an option.
C++
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <string>
#include "coreclrhost.h"
#include <iostream>
#define MANAGED_ASSEMBLY "TestConsole.dll"
#include <dirent.h>
#include <dlfcn.h>
#include <limits.h>
#define FS_SEPARATOR "/"
#define PATH_DELIMITER ":"
#define MAX_PATH PATH_MAX
#define CORECLR_FILE_NAME "libcoreclr.so"
// Function pointer types for the managed call and callback
typedef int (*report_callback_ptr)(int progress);
typedef char* (*doWork_ptr)(const char* jobName, int iterations, int dataSize, double* data, report_callback_ptr callbackFunction);
typedef int (*Multiply5_ptr)(const int i);
typedef (*Constructor_ptr)(int i1, int i2);
typedef int (*ReturnInt_ptr)();
void BuildTpaList(const char* directory, const char* extension, std::string& tpaList);
int main(int argc, char* argv[])
{
// Get the current executable's directory
// This sample assumes that both CoreCLR and the
// managed assembly to be loaded are next to this host
// so we need to get the current path in order to locate those.
char runtimePath[MAX_PATH];
#if WINDOWS
GetFullPathNameA(argv[0], MAX_PATH, runtimePath, NULL);
#elif LINUX
realpath(argv[0], runtimePath);
#endif
char *last_slash = strrchr(runtimePath, FS_SEPARATOR[0]);
if (last_slash != NULL)
*last_slash = 0;
// Construct the CoreCLR path
// For this sample, we know CoreCLR's path. For other hosts,
// it may be necessary to probe for coreclr.dll/libcoreclr.so
std::string coreClrPath(runtimePath);
coreClrPath.append(FS_SEPARATOR);
coreClrPath.append(CORECLR_FILE_NAME);
// Construct the managed library path
std::string managedLibraryPath(runtimePath);
managedLibraryPath.append(FS_SEPARATOR);
managedLibraryPath.append(MANAGED_ASSEMBLY);
//
// STEP 1: Load CoreCLR (coreclr.dll/libcoreclr.so)
//
#if WINDOWS
// <Snippet1>
HMODULE coreClr = LoadLibraryExA(coreClrPath.c_str(), NULL, 0);
// </Snippet1>
#elif LINUX
void *coreClr = dlopen(coreClrPath.c_str(), RTLD_NOW | RTLD_LOCAL);
#endif
if (coreClr == NULL)
{
printf("ERROR: Failed to load CoreCLR from %s\n", coreClrPath.c_str());
return -1;
}
else
{
printf("Loaded CoreCLR from %s\n", coreClrPath.c_str());
}
//
// STEP 2: Get CoreCLR hosting functions
//
#if WINDOWS
// <Snippet2>
coreclr_initialize_ptr initializeCoreClr = (coreclr_initialize_ptr)GetProcAddress(coreClr, "coreclr_initialize");
coreclr_create_delegate_ptr createManagedDelegate = (coreclr_create_delegate_ptr)GetProcAddress(coreClr, "coreclr_create_delegate");
coreclr_shutdown_ptr shutdownCoreClr = (coreclr_shutdown_ptr)GetProcAddress(coreClr, "coreclr_shutdown");
// </Snippet2>
#elif LINUX
coreclr_initialize_ptr initializeCoreClr = (coreclr_initialize_ptr)dlsym(coreClr, "coreclr_initialize");
coreclr_create_delegate_ptr createManagedDelegate = (coreclr_create_delegate_ptr)dlsym(coreClr, "coreclr_create_delegate");
coreclr_shutdown_ptr shutdownCoreClr = (coreclr_shutdown_ptr)dlsym(coreClr, "coreclr_shutdown");
#endif
if (initializeCoreClr == NULL)
{
printf("coreclr_initialize not found");
return -1;
}
if (createManagedDelegate == NULL)
{
printf("coreclr_create_delegate not found");
return -1;
}
if (shutdownCoreClr == NULL)
{
printf("coreclr_shutdown not found");
return -1;
}
//
// STEP 3: Construct properties used when starting the runtime
//
// Construct the trusted platform assemblies (TPA) list
// This is the list of assemblies that .NET Core can load as
// trusted system assemblies.
// For this host (as with most), assemblies next to CoreCLR will
// be included in the TPA list
std::string tpaList;
BuildTpaList(runtimePath, ".dll", tpaList);
tpaList.append(managedLibraryPath);
tpaList.append(":");
// <Snippet3>
// Define CoreCLR properties
// Other properties related to assembly loading are common here,
// but for this simple sample, TRUSTED_PLATFORM_ASSEMBLIES is all
// that is needed. Check hosting documentation for other common properties.
const char* propertyKeys[] = {
"TRUSTED_PLATFORM_ASSEMBLIES" // Trusted assemblies
};
const char* propertyValues[] = {
tpaList.c_str()
};
// </Snippet3>
//
// STEP 4: Start the CoreCLR runtime
//
// <Snippet4>
void* hostHandle;
unsigned int domainId;
// This function both starts the .NET Core runtime and creates
// the default (and only) AppDomain
int hr = initializeCoreClr(
runtimePath, // App base path
"SampleHost", // AppDomain friendly name
sizeof(propertyKeys) / sizeof(char*), // Property count
propertyKeys, // Property names
propertyValues, // Property values
&hostHandle, // Host handle
&domainId); // AppDomain ID
// </Snippet4>
if (hr >= 0)
{
printf("CoreCLR started\n");
}
else
{
printf("coreclr_initialize failed - status: 0x%08x\n", hr);
return -1;
}
//
// STEP 5: Create delegate to managed code and invoke it
//
// <Snippet5>
Multiply5_ptr managedDelegate;
// The assembly name passed in the third parameter is a managed assembly name
// as described at https://learn.microsoft.com/dotnet/framework/app-domains/assembly-names
hr = createManagedDelegate(
hostHandle,
domainId,
"TestConsole, Version=1.0.0.0",
"TestConsole.Program",
"Multiply5",
(void**)&managedDelegate);
// </Snippet5>
if (hr >= 0)
{
printf("Managed delegate created\n");
}
else
{
printf("coreclr_create_delegate failed - status: 0x%08x\n", hr);
return -1;
}
int i = 20;
// Invoke the managed delegate and write the returned intS to the console
//char* ret = managedDelegate("Test job", 1, sizeof(int), i, ReportProgressCallback);
int ret = managedDelegate(i);
printf("Managed code returned: %d\n", ret);
Constructor_ptr programDelegate;
hr = createManagedDelegate(hostHandle,
domainId,
"TestConsole, Version=1.0.0.0",
"TestConsole.Program",
"Program",
(void**)&programDelegate);
int i1 = i;
int i2 = ret;
programDelegate(i1,i2);//Will seg fault here
ReturnInt_ptr addDelegate;
hr = createManagedDelegate(hostHandle,
domainId,
"TestConsole, Version=1.0.0.0",
"TestConsole.Program",
"Add",
(void**)&addDelegate);
i = addDelegate(); //Also triggers a seg fault.
printf("Managed code returned: %d\n", i);
// Strings returned to native code must be freed by the native code
#if WINDOWS
CoTaskMemFree(ret);
#elif LINUX
// free(ret);
#endif
//
// STEP 6: Shutdown CoreCLR
//
// <Snippet6>
hr = shutdownCoreClr(hostHandle, domainId);
// </Snippet6>
if (hr >= 0)
{
printf("CoreCLR successfully shutdown\n");
}
else
{
printf("coreclr_shutdown failed - status: 0x%08x\n", hr);
}
return 0;
}
#if WINDOWS
// Win32 directory search for .dll files
// <Snippet7>
void BuildTpaList(const char* directory, const char* extension, std::string& tpaList)
{
// This will add all files with a .dll extension to the TPA list.
// This will include unmanaged assemblies (coreclr.dll, for example) that don't
// belong on the TPA list. In a real host, only managed assemblies that the host
// expects to load should be included. Having extra unmanaged assemblies doesn't
// cause anything to fail, though, so this function just enumerates all dll's in
// order to keep this sample concise.
std::string searchPath(directory);
searchPath.append(FS_SEPARATOR);
searchPath.append("*");
searchPath.append(extension);
WIN32_FIND_DATAA findData;
HANDLE fileHandle = FindFirstFileA(searchPath.c_str(), &findData);
if (fileHandle != INVALID_HANDLE_VALUE)
{
do
{
// Append the assembly to the list
tpaList.append(directory);
tpaList.append(FS_SEPARATOR);
tpaList.append(findData.cFileName);
tpaList.append(PATH_DELIMITER);
// Note that the CLR does not guarantee which assembly will be loaded if an assembly
// is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll
// extensions. Therefore, a real host should probably add items to the list in priority order and only
// add a file if it's not already present on the list.
//
// For this simple sample, though, and because we're only loading TPA assemblies from a single path,
// and have no native images, we can ignore that complication.
}
while (FindNextFileA(fileHandle, &findData));
FindClose(fileHandle);
}
}
// </Snippet7>
#elif LINUX
// POSIX directory search for .dll files
void BuildTpaList(const char* directory, const char* extension, std::string& tpaList)
{
DIR* dir = opendir(directory);
struct dirent* entry;
int extLength = strlen(extension);
while ((entry = readdir(dir)) != NULL)
{
// This simple sample doesn't check for symlinks
std::string filename(entry->d_name);
// Check if the file has the right extension
int extPos = filename.length() - extLength;
if (extPos <= 0 || filename.compare(extPos, extLength, extension) != 0)
{
continue;
}
// Append the assembly to the list
tpaList.append(directory);
tpaList.append(FS_SEPARATOR);
tpaList.append(filename);
tpaList.append(PATH_DELIMITER);
// Note that the CLR does not guarantee which assembly will be loaded if an assembly
// is in the TPA list multiple times (perhaps from different paths or perhaps with different NI/NI.dll
// extensions. Therefore, a real host should probably add items to the list in priority order and only
// add a file if it's not already present on the list.
//
// For this simple sample, though, and because we're only loading TPA assemblies from a single path,
// and have no native images, we can ignore that complication.
}
}
#endif
C#
namespace TestConsole
{
public class Program
{
IntTest i;
Program(int i1, int i2){
i = new IntTest(i1,i2);
}
public static void Main()
{
Program p = new Program(23,12);
Console.WriteLine(p.Add());
}
// This test method doesn't actually do anything, it just takes some input parameters,
// waits (in a loop) for a bit, invoking the callback function periodically, and
// then returns a string version of the double[] passed in.
//[return: MarshalAs(UnmanagedType.I4)]
public static int Return5(){
return 5;
}
public int Add(){
return i.Add();
}
private static int Multiply5(int i){
return 5*i;
}
}
}
IntTest is an external library.
So there is no way to do this all free c++, the other option is to wrap the C# method in a static one and create a function pointer that way.

We are not able to set customized passwordFilter in windows 10

I created my own password filter.dll, but I am not able to set it into Windows system.
I appended it under LSA->Notificationpackage, and I copied into c:\Window\System32 as well.
In my password filter, at start I created the log file, but it is not creating it.
Please let me know is there are any more steps I need to perform to set my password filter in Windows 10.
Please find this snippet of my password filter:
#include <regex>
#include <fstream>
#include "Ntsecapi.h"
#define MAX_SIZE 4028
using namespace std;
using namespace std::tr1;
fstream writeLog;
BOOLEAN __stdcall InitializeChangeNotify(void)
{
wchar_t *pLogFile = L"c:\AmitPasswordFilter.log";
wchar_t aLogFileExp[64];
ExpandEnvironmentStrings(pLogFile, aLogFileExp, sizeof(aLogFileExp)/sizeof(wchar_t));
writeLog.open(aLogFileExp, ios::out|ios::app);
writeLog<<"InitializeChangeNotify"<<endl;
return TRUE;
}
BOOLEAN __stdcall PasswordFilter(PUNICODE_STRING AccountName, PUNICODE_STRING FullName, PUNICODE_STRING Password, BOOLEAN SetOperation)
{
writeLog<<"BabaPasswordFilter"<<endl;
writeLog<< "PasswordFilterAmit"<<endl;
wcmatch mr;
BOOL match = FALSE;
std::wstring seperator(L")(?=");
std::wstring regExp(L"(?=");
const int cathegories=4;
unsigned int aRegCondition[cathegories]={2,2,2,2};
for(int i=0;i<cathegories;i++)
{
for(int i=0;i<aRegCondition[0];i++)
regExp+=L".*\\d";
regExp+=seperator;
for(int i=0;i<aRegCondition[1];i++)
regExp+=L".*\\W";
regExp+=seperator;
for(int i=0;i<aRegCondition[2];i++)
regExp+=L".*[A-Z]";
regExp+=seperator;
for(int i=0;i<aRegCondition[3];i++)
regExp+=L".*[a-z]";
regExp+=L")(?![.\\n]).*$";// check for newline characters and end
wregex rx(regExp);
if (Password)
{
match = regex_search(Password->Buffer, mr, rx);
if (match)
{
match = TRUE;
writeLog<<"SumitPassword matches the complexity"<<endl;
}
else
{
match = FALSE;
writeLog<<"SumitPassword does not matche the complexity"<<endl;
}
}
else
{
writeLog<<"SumitPassword is NULL"<<endl;
}
return match;
}
Does your domain password policy, do you have "Passwords must meet complexity requirements" enabled? Per Microsoft's documentation on enabling a custom password filter, this policy needs to be enabled for the password filter to be applied (when we implemented our password filter five or so years ago, I confirmed the policy was required too).
If you've got the complexity policy enabled, run a remote debugging session against LSASS.EXE (in WinDBG, you need to close the debugging session with 'qd' {quit and detach} otherwise lsass.exe terminates unexpectedly and your computer is all sorts of messed up until it reboots).
I would recommend not putting anything under InitializeChangeNotify unless you have thorough exception handling. Exceptions occurring through this call can create wide-spread issues. My InitializeChangeNotify is simply a return true ... which is accurate since my filter has a separate service which applies the business logic to test password validity -- if my DLL is up enough to respond to InitializeChangeNotify, then it is up.
For reference, my PasswordFilter function:
extern "C" __declspec(dllexport) BOOLEAN __stdcall PasswordFilter(PUNICODE_STRING AccountName,
PUNICODE_STRING FullName,
PUNICODE_STRING Password,
BOOLEAN SetOperation) {
//build the account struct
PasswordFilterAccount *pfAccount = new PasswordFilterAccount();
pfAccount->AccountName = AccountName;
pfAccount->Password = Password;
//start an asynchronous thread to be able to kill the thread if it exceeds the timout
HANDLE pfHandle = (HANDLE)_beginthreadex(0, 0, CreateSocket, (LPVOID *)pfAccount, 0, 0);
// timeout is milliseconds. Is 30 seconds too long?
DWORD dWaitFor = WaitForSingleObject(pfHandle, 30000); //do not exceed the timeout.
if (dWaitFor == WAIT_TIMEOUT) {
//timeout exceeded
writeWindowsEventLog("Timeout exceeded", "OPF", "ERROR", 5);
}
else if (dWaitFor == WAIT_OBJECT_0) {
//here is where we want to be
}
else {
//WAIT_ABANDONED
//WAIT_FAILED
writeWindowsEventLog("WAIT abandoned or failed", "OPF", "ERROR", 5);
}
if (pfHandle != INVALID_HANDLE_VALUE && pfHandle != 0) {
if (CloseHandle(pfHandle)) {
pfHandle = INVALID_HANDLE_VALUE;
}
}
return bPasswordOk;
}

Memory leak with multi threaded c++ application on ssl certificate validation

We are using openssl library in our multi threaded c++ application. The application eats up all the memory within 3 days (7 GB instance) due to a memory leak, only if SSL certificate validation is enabled.
Please find my application flow here:
On app launch, We create 150 threads for syncing 30k users data and reserving one SSL_CTX_new object per thread. The same object is reused until the process is killed. The object SSL_CTX_new is created only once on thread initialization, it will be reused for all subsequent ssl connections.
We do the following on processing user's data:
A thread creates a new socket connection to the third party server via ssl and once the required data is fetched from the 3rd party server, ssl connection is terminated.
Similarly all threads pick up one user at a time from the queue and fetches the data from the server.
We have to do above connect, fetch data and disconnect ssl connections for all 30k users.
Please find pseudocode example of our application:
ThreadTask()
{
ssldata* ssl_data;
1. Creates SSL connection: user_ssl_new_connect(ssl_data)
2. Fetch users data
3. Terminate ssl connection: ssl_abort()
}
char* user_ssl_new_connect(ssldata* ssl_data) {
SSL_CTX *ssl_context = InitsslonePerThread();
if (!ssl_context) {
if (!(ssl_context = SSL_CTX_new (SSLv23_client_method ()) {
retur NULL;
}
}
SSL_CTX_set_options (ssl_context,SSL_OP_NO_COMPRESSION|SSL_MODE_RELEASE_BUFFERS);
SSL_CTX_set_verify (ssl_context,SSL_VERIFY_PEER,ssl_open_verify);
SSL_CTX_set_default_verify_paths (ssl_context);
char * s = "sslpath"
SSL_CTX_load_verify_locations (ssl_context,s,NIL);
SetsslconnectionPerThread(ssl_context);
if (!(ssl_data->sslconnection = (SSL *) SSL_new (ssl_context)))
return NULL
bio = BIO_new_socket (ssl_data->sockettcpsi,BIO_NOCLOSE);
SSL_set_bio (ssl_data->sslconnection,bio,bio);
SSL_set_connect_state(ssl_data->sslconnection);
if (SSL_in_init(ssl_data->sslconnection)) SSL_total_renegotiations (ssl_data->sslconnection);
/* now negotiate SSL */
if ((retval = SSL_write (ssl_data->sslconnection,"",0)) < 0) {
return NULL
}
/* validating host names? */
if ((err = ssl_validate_cert (cert = SSL_get_peer_certificate (sslconnection),host))) {
return NULL;
}
}
// one ssl_context per thread in global variable
ssl_context* InitsslonePerThread() {
yULong threadid;
threadid = (unsigned long) pthread_self();
if ssl_context is not created for this threadid
returns new ssl_context.
else
returns previous ssl_context.
}
void SetsslconnectionPerThread(ssl_context*) {
yULong threadid;
threadid = (unsigned long) pthread_self();
#setting ssl_context in global variable
}
long ssl_abort (ssldata* ssl_data)
{
if (ssl_data->sslconnection) { /* close SSL connection */
SSL_shutdown (ssl_data->sslconnection);
SSL_free (ssl_data->sslconnection);
}
return NIL;
}
static char *ssl_validate_cert (X509 *cert,char *host)
{
int i,n;
char *s,*t,*ret;
void *ext;
GENERAL_NAME *name;
char tmp[MAILTMPLEN];
/* make sure have a certificate */
if (!cert) ret = "No certificate from server";
/* and that it has a name */
else if (!cert->name) ret = "No name in certificate";
/* locate CN */
else if (s = strstr (cert->name,"/CN=")) {
if (t = strchr (s += 4,'/')) *t = '\0';
/* host name matches pattern? */
ret = ssl_compare_hostnames (host,s) ? NIL :
"Server name does not match certificate";
if (t) *t = '/'; /* restore smashed delimiter */
/* if mismatch, see if in extensions */
if (ret && (ext = X509_get_ext_d2i (cert,NID_subject_alt_name,NIL,NIL)) &&
(n = sk_GENERAL_NAME_num (ext)))
/* older versions of OpenSSL use "ia5" instead of dNSName */
for (i = 0; ret && (i < n); i++)
if ((name = sk_GENERAL_NAME_value (ext,i)) &&
(name->type = GEN_DNS) && (s = name->d.ia5->data) &&
ssl_compare_hostnames (host,s)) ret = NIL;
} else ret = "Unable to locate common name in certificate";
return ret;
}
if ((err = ssl_validate_cert (cert = SSL_get_peer_certificate (sslconnection),host))) {
return NULL;
}
You must free the X509* returned from SSL_get_peer_certificate using X509_free. There may be more leaks, but that one seems consistent with the description of your issue.
The memory leak is also why OpenSSL's TLS Client example immediately free's the X509*. Its reference counted, so its safe to decrement the count and use the X509* until the session is destroyed (the SSL*).
X509* cert = SSL_get_peer_certificate(ssl);
if(cert) { X509_free(cert); } /* Free immediately */
if(NULL == cert) handleFailure();
...
You may also be leaking some of the names returned from the name walk. I don't use IA5 strings, so I'm not certain. I use UTF8 strings, and they must be freed.
Here's some unrelated comments... You should probably include SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3:
SSL_CTX_set_options (ssl_context,SSL_OP_NO_COMPRESSION|SSL_MODE_RELEASE_BUFFERS);
You should also probably specify SSL_set_tlsext_host_name somewhere to use SNI for hosted environments, where the default site certificate may not be the target site's certificate. SNI is a TLS extension, so it speaks to the need for SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3.
You should also test the application well when using SSL_MODE_RELEASE_BUFFERS. I seem to recall it caused memory errors. Also see Issue 2167: OpenSSL fails if used from multiple threads and with SSL_MODE_RELEASE_BUFFERS, CVE-2010-5298, and Adam Langley's Overclocking SSL.
The sample program provided on the wiki TLS Client may also help you with you name matching. The best I can tell, the code is vulnerable to Marlinspike's embedded NULL tricks. See his Blackhat talk at More Tricks For Defeating SSL In Practice for more details.
This is just an observation... Since you are using C++, why are you not using smart pointers to manage your resources? Something like this works very well in practice, and it would have fixed the leak because X509_ptr has the destructor function specified:
X509_ptr cert(SSL_get_peer_certificate(ssl));
// Use cert, its free'd automatically
char* name = ssl_validate_cert(cert.get(), "www.example.com");
And:
using EC_KEY_ptr = std::unique_ptr<EC_KEY, decltype(&::EC_KEY_free)>;
using EC_GROUP_ptr = std::unique_ptr<EC_GROUP, decltype(&::EC_GROUP_free)>;
using EC_POINT_ptr = std::unique_ptr<EC_POINT, decltype(&::EC_POINT_free)>;
using DH_ptr = std::unique_ptr<DH, decltype(&::DH_free)>;
using RSA_ptr = std::unique_ptr<RSA, decltype(&::RSA_free)>;
using DSA_ptr = std::unique_ptr<DSA, decltype(&::DSA_free)>;
using EVP_PKEY_ptr = std::unique_ptr<EVP_PKEY, decltype(&::EVP_PKEY_free)>;
using BN_ptr = std::unique_ptr<BIGNUM, decltype(&::BN_free)>;
using FILE_ptr = std::unique_ptr<FILE, decltype(&::fclose)>;
using BIO_MEM_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
using BIO_FILE_ptr = std::unique_ptr<BIO, decltype(&::BIO_free)>;
using EVP_MD_CTX_ptr = std::unique_ptr<EVP_MD_CTX, decltype(&::EVP_MD_CTX_destroy)>;
using X509_ptr = std::unique_ptr<X509, decltype(&::X509_free)>;
using ASN1_INTEGER_ptr = std::unique_ptr<ASN1_INTEGER, decltype(&::ASN1_INTEGER_free)>;
using ASN1_TIME_ptr = std::unique_ptr<ASN1_TIME, decltype(&::ASN1_TIME_free)>;
using X509_EXTENSION_ptr = std::unique_ptr<X509_EXTENSION, decltype(&::X509_EXTENSION_free)>;
using X509_NAME_ptr = std::unique_ptr<X509_NAME, decltype(&::X509_NAME_free)>;
using X509_NAME_ENTRY_ptr = std::unique_ptr<X509_NAME_ENTRY, decltype(&::X509_NAME_ENTRY_free)>;
using X509_STORE_ptr = std::unique_ptr<X509_STORE, decltype(&::X509_STORE_free)>;
using X509_LOOKUP_ptr = std::unique_ptr<X509_LOOKUP, decltype(&::X509_LOOKUP_free)>;
using X509_STORE_CTX_ptr = std::unique_ptr<X509_STORE_CTX, decltype(&::X509_STORE_CTX_free)>;

Command Line C++ Program to Send an EMail

I'm using VS2008 & C++ and I'm trying to create a command line program that sends an email.
I've looked on line and found some sample programs but none will compile for me.
Does anyone have an example program for me?
Thanks
This code compiles & runs for me - after figuring out the right headers etc. Still needs command line handling, and the use of the MAPI libraries is deprecated, but what do you want for free? Original code from codeproject.com
#include "windows.h"
#include "tchar.h"
#include "mapi.h"
#include "assert.h"
#define ASSERT assert
#define VERIFY assert
BOOL SendMail(CHAR *lpszFrom, CHAR *lpszTo, CHAR *lpszSubject, CHAR *lpszMessage)
{
BOOL bSent = FALSE;
HINSTANCE hMAPI = ::LoadLibrary(_T("mapi32.dll"));
if(0==hMAPI) return bSent;
typedef ULONG (FAR PASCAL *PFN_MAPILogon)(ULONG,LPTSTR,LPTSTR,FLAGS,ULONG,LPLHANDLE);
typedef ULONG (FAR PASCAL *PFN_MAPISendMail)(LHANDLE,ULONG,lpMapiMessage,FLAGS,ULONG);
typedef ULONG (FAR PASCAL *PFN_MAPILogoff)(LHANDLE,ULONG,FLAGS,ULONG);
PFN_MAPILogon MAPILogon = (PFN_MAPILogon)::GetProcAddress(hMAPI,"MAPILogon");
PFN_MAPISendMail MAPISendMail = (PFN_MAPISendMail)::GetProcAddress(hMAPI,"MAPISendMail");
PFN_MAPILogoff MAPILogoff = (PFN_MAPILogoff)::GetProcAddress(hMAPI,"MAPILogoff");
const BOOL bFunctionsLoaded = (0!=MAPILogon)&&(0!=MAPISendMail)&&(0!=MAPILogoff);
ASSERT(bFunctionsLoaded);
if(bFunctionsLoaded)
{
LHANDLE session = 0;
VERIFY(SUCCESS_SUCCESS==MAPILogon(0,0,0,MAPI_NEW_SESSION,0,&session));
ASSERT(0!=session);
MapiRecipDesc recipient;
::ZeroMemory(&recipient,sizeof(recipient));
recipient.ulRecipClass = MAPI_TO;
recipient.lpszName = lpszTo;
MapiMessage message;
::ZeroMemory(&message,sizeof(message));
message.lpszSubject = lpszSubject;
message.lpszNoteText = lpszMessage;
message.nRecipCount = 1;
message.lpRecips = &recipient;
bSent = SUCCESS_SUCCESS == MAPISendMail(session,0,&message,0,0);
VERIFY(SUCCESS_SUCCESS==MAPILogoff(session,0,0,0));
}
::FreeLibrary(hMAPI);
return bSent;
}
int _tmain(int argc, _TCHAR* argv[])
{
SendMail("from_you#go_daddy.com","to.someone#gmail.com","Test subject","New Message");
return 0;
}
Take a look at this: http://sourceforge.net/projects/blat/files/
Since your server is Exchange, your most convenient method to write a program to send email will be using C# and System.Net.Mail as demonstrated here. Here is the C++/CLI code:
static void CreateTestMessage2( String^ server )
{
String^ to = L"jane#contoso.com";
String^ from = L"ben#contoso.com";
MailMessage^ message = gcnew MailMessage( from,to );
message->Subject = L"Using the new SMTP client.";
message->Body = L"Using this new feature, you can send an e-mail message from an application very easily.";
SmtpClient^ client = gcnew SmtpClient( server );
// Credentials are necessary if the server requires the client
// to authenticate before it will send e-mail on the client's behalf.
client->UseDefaultCredentials = true;
client->Send( message );
client->~SmtpClient();
}
If you really want to use native C++ (ie. not access System.Net.Mail via C++/CLI) then you are stuck with one of the native APIs described here.
However you could use MapiSend or blat as described here.