Security
While bypassing the Security Patch to allow for an unrestricted access to the Outlook objects, Redemption does provide several layers of security to help you minimize a change of a rogue code using taking advantage of Redemption.
Using AuthKey property. Once AuthKey property is set to some value, all Redemption objects must set AuthKey property to the same value before any other properties can be used:
|
Dim sContact, oContact
set sContact = CreateObject("Redemption.SafeContactItem") sContact.AuthKey = "SecretKey" set oContact = Application.Session.GetDefaultFolder(10).Items(1) sContact.Item = oContact MsgBox sContact.Email1Address |
If you want to change the value of the AuthKey property, you need to first set it to the old value (so that Redemption knows that you are a legitimate user), then reset it to a new value. Or you can delete HKCU\Redemption registry key (note a couple funny looking characters - Redemption uses a Unicode name to make the key inaccessible to the scripts). The registry keys contain hashes of the paths to either original or customized copies of Redemption.dll and hashes of the authentication keys; since only hashes are stored, there is no way to deduct the real values of the dll paths and authentication keys.
|
Dim sContact, oContact
|
Create a truly custom version of Redemption with custom class names and GUIDs. Distributable version of Redemption comes with a customization tool (customize.exe) that lets you create a custom copy of Redemption (it is not available in the demo version). AuthKey property (see above) will only apply to your copy of Redemption: your code will not be affected by other instances of the Redemption library installed by other applications, as well as other applications will not even be aware of your customized version of Redemption.

Custom
versions of Redemption are guaranteed not to interact with other instances
of Redemption, either original or custom. Creating a custom version of
Redemption along with using the AuthKey property significantly reduces a
chance of malicious code using Redemption.
Note that to use a custom version of Redemption, your VB code must use CreateObject() function rather than New.
E.g. the code like
|
Dim sItem as Redemption.SafeMailItem |
|
Dim sItem as Redemption.SafeMailItem |
On the
low level, in the first example VB hardcodes the class GUID of
Redemption.SafeMailItem and if you modify the dll name and/or class name the
second line will fail.
If you
use the second example, VB does not hardcode the class GUID. I.e. you still
dim your variable as Redemption.SafeMailItem (since it is only used at
design time by VB), but to create an instance of the object, you must
specify the modified class name ("MyDll.MyMailItem")
If you
are using C++, this means that you need to either use CoCreateInstance() passing
the modified class GUID or call CLSIDFromProgID() to obtain the
(modified) class GUID, then call CoCreateInstance() In case of C# or other .Net languages, you can use the code similar to
that given below (it is assumed that you have already created the interop
DLL):
Type t = Type.GetTypeFromProgID("MyDll.MyMailItem"); Note: Redemption installs itself as an Exchange Client
Extension (ECE) for the performance reasons (it retrieves IMAPISession
directly from Outlook for use by the Safe*Item objects). If your code runs
outside the outlook.exe process (i.e. it is not an Outlook COM add-in), you
will not benefit from Redemption being an ECE. In such a case, you can turn
the ECE functionality off by un-checking the ""Install Redemption as an
Exchange Client Extension" checkbox (see step 4 on the screenshot above). An
additional benefit is that you will be able to replace the dll even if
Outlook is running since it only loads Redemption.dll if Redemption is
installed as an ECE.
SafeMailItem sItem = (SafeMailItem) Activator.CreateInstance(t);
Use Registry-free COM (Windows XP or later) (See "Creating Registration-Free COM Objects" on MSDN and "Simplify App Deployment with ClickOnce and Registration-Free COM" in MSDN Magazine). Windows XP allows COM objects to be used without requiring that they be registered. If your application uses a manifest file, you can request that you want Windows to load the specified COM objects from the directory where your executable resides rather than query the registry for the location of the COM library.
To use this feature add (or modify) the manifest file and provide the following (bolded) entries. Note that registry-free COM can be used with other methods discussed above
|
<?xml version="1.0" encoding="utf-8"?> |
Similar to registry-free COM, you can explicitly load Redemption library and create instances of its creatable objects without registering the dll in the registry or even using an application manifest. On the low level, all in-proc COM libraries (dlls) export DllGetClassObject function that the COM system uses after looking up the dll location through the class name/CLSID in the registry. Since you know the Redemption dll location, there is no reason to create any registry entries for the COM system. You will still need to register Redemption in the registry on your development machine to be able to import the type library and/or create the interop dll (in case of .Net languages). At run-time however, you can simply copy Redemption.dll/Redemption64.dll to the target folder.
//tell the app where the 32 and 64 bit dlls are located
//by default, they are assumed to be in the same
folder as the current assembly and be named
//Redemption.dll
and Redemption64.dll
RedemptionLoader.DllLocation64Bit =
@"c:\SourceCode\Redemption\redemption64.dll";
RedemptionLoader.DllLocation32Bit =
@"c:\SourceCode\Redemption\redemption.dll";
//Create a Redemption object and use it
RDOSession
session =
RedemptionLoader.new_RDOSession();
session.Logon(Missing.Value,
Missing.Value,
Missing.Value, Missing.Value,
Missing.Value,
Missing.Value);
RedemptionLoader class can be downloaded here. Its full source code is also given below:
using
System;
using
System.Collections.Generic;
using
System.Text;
using
System.IO;
using
System.Reflection;
using
System.Runtime.InteropServices;
using
System.Runtime.InteropServices.ComTypes;
namespace
Redemption
{
public static
class
RedemptionLoader
{
#region public methods
//64 bit dll location - defaults to <assemblydir>\Redemption64.dll
public static
string DllLocation64Bit;
//32 bit dll location - defaults to <assemblydir>\Redemption.dll
public static
string DllLocation32Bit;
//The only creatable RDO object - RDOSession
public static
RDOSession new_RDOSession()
{
return
(RDOSession)NewRedemptionObject(new
Guid("29AB7A12-B531-450E-8F7A-EA94C2F3C05F"));
}
//Safe*Item objects
public static
SafeMailItem new_SafeMailItem()
{
return (SafeMailItem)NewRedemptionObject(new
Guid("741BEEFD-AEC0-4AFF-84AF-4F61D15F5526"));
}
public static
SafeContactItem new_SafeContactItem()
{
return (SafeContactItem)NewRedemptionObject(new
Guid("4FD5C4D3-6C15-4EA0-9EB9-EEE8FC74A91B"));
}
public
static
SafeAppointmentItem new_SafeAppointmentItem()
{
return (SafeAppointmentItem)NewRedemptionObject(new
Guid("620D55B0-F2FB-464E-A278-B4308DB1DB2B"));
}
public static
SafeTaskItem new_SafeTaskItem()
{
return (SafeTaskItem)NewRedemptionObject(new
Guid("7A41359E-0407-470F-B3F7-7C6A0F7C449A"));
}
public static
SafeJournalItem new_SafeJournalItem()
{
return (SafeJournalItem)NewRedemptionObject(new
Guid("C5AA36A1-8BD1-47E0-90F8-47E7239C6EA1"));
}
public static
SafeMeetingItem new_SafeMeetingItem()
{
return (SafeMeetingItem)NewRedemptionObject(new
Guid("FA2CBAFB-F7B1-4F41-9B7A-73329A6C1CB7"));
}
public static
SafePostItem new_SafePostItem()
{
return (SafePostItem)NewRedemptionObject(new
Guid("11E2BC0C-5D4F-4E0C-B438-501FFE05A382"));
}
public static
SafeReportItem new_SafeReportItem()
{
return (SafeReportItem)NewRedemptionObject(new
Guid("D46BA7B2-899F-4F60-85C7-4DF5713F6F18"));
}
public static
MAPIFolder new_MAPIFolder()
{
return (MAPIFolder)NewRedemptionObject(new
Guid("03C4C5F4-1893-444C-B8D8-002F0034DA92"));
}
public static
SafeCurrentUser new_SafeCurrentUser()
{
return (SafeCurrentUser)NewRedemptionObject(new
Guid("7ED1E9B1-CB57-4FA0-84E8-FAE653FE8E6B"));
}
public static
SafeDistList new_SafeDistList()
{
return (SafeDistList)NewRedemptionObject(new
Guid("7C4A630A-DE98-4E3E-8093-E8F5E159BB72"));
}
public static
AddressLists newAddressLists()
{
return (AddressLists)NewRedemptionObject(new
Guid("37587889-FC28-4507-B6D3-8557305F7511"));
}
public static
MAPITable new_MAPITable()
{
return (MAPITable)NewRedemptionObject(new
Guid("A6931B16-90FA-4D69-A49F-3ABFA2C04060"));
}
public static
MAPIUtils new_MAPIUtils()
{
return (MAPIUtils)NewRedemptionObject(new
Guid("4A5E947E-C407-4DCC-A0B5-5658E457153B"));
}
public static
SafeInspector new_SafeInspector()
{
return (SafeInspector)NewRedemptionObject(new
Guid("ED323630-B4FD-4628-BC6A-D4CC44AE3F00"));
}
#endregion
#region private methods
static RedemptionLoader()
{
//default locations of the dlls
string path =
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
DllLocation64Bit = string.Concat(path,
@"\Redemption64.dll");
DllLocation32Bit = string.Concat(path,
@"\Redemption.dll");
}
/*
static ~Loader()
{
if (!_redemptionDllHandle.Equals(IntPtr.Zero))
{
IntPtr dllCanUnloadNowPtr = Win32NativeMethods.GetProcAddress(_redemptionDllHandle,
"DllCanUnloadNow");
if (!dllCanUnloadNowPtr.Equals(IntPtr.Zero))
{
DllCanUnloadNow dllCanUnloadNow = (DllCanUnloadNow)Marshal.GetDelegateForFunctionPointer(dllCanUnloadNowPtr,
typeof(DllCanUnloadNow));
if (dllCanUnloadNow() != 0) return; //there are still live objects
returned by the dll, so we shoudl not unload the dll
}
Win32NativeMethods.FreeLibrary(_redemptionDllHandle);
_redemptionDllHandle = IntPtr.Zero;
}
}
*/
[ComVisible(false)]
[ComImport,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("00000001-0000-0000-C000-000000000046")]
private interface
IClassFactory
{
void CreateInstance([MarshalAs(UnmanagedType.Interface)]
object pUnkOuter, ref
Guid refiid, [MarshalAs(UnmanagedType.Interface)]
out object ppunk);
void LockServer(bool
fLock);
}
[ComVisible(false)]
[ComImport,
InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
Guid("00000000-0000-0000-C000-000000000046")]
private interface
IUnknown
{
}
private delegate
int
DllGetClassObject(ref
Guid ClassId, ref
Guid InterfaceId, [Out,
MarshalAs(UnmanagedType.Interface)]
out object ppunk);
private delegate
int DllCanUnloadNow();
//COM GUIDs
private static
Guid IID_IClassFactory =
new Guid("00000001-0000-0000-C000-000000000046");
private static
Guid IID_IUnknown =
new Guid("00000000-0000-0000-C000-000000000046");
//win32 functions to load\unload dlls and get a
function pointer
private class
Win32NativeMethods
{
[DllImport("kernel32.dll",
CharSet=CharSet.Ansi)]
public static
extern IntPtr
GetProcAddress(IntPtr hModule,
string lpProcName);
[DllImport("kernel32.dll")]
public static
extern bool
FreeLibrary(IntPtr hModule);
[DllImport("kernel32.dll",
CharSet = CharSet.Unicode)]
public static
extern IntPtr
LoadLibraryW(string lpFileName);
}
//private variables
private static
IntPtr _redemptionDllHandle =
IntPtr.Zero;
private static
IntPtr _DllGetClassObject =
IntPtr.Zero;
private static
IUnknown NewRedemptionObject(Guid
guid)
{
object res =
null;
IClassFactory ClassFactory;
if (_redemptionDllHandle.Equals(IntPtr.Zero))
{
string dllPath;
if (IntPtr.Size
== 8) dllPath = DllLocation64Bit; else dllPath =
DllLocation32Bit;
_redemptionDllHandle = Win32NativeMethods.LoadLibraryW(dllPath);
if (_redemptionDllHandle.Equals(IntPtr.Zero))
throw new
Exception(string.Format("Could
not load '{0}'\nMake sure the dll exists.", dllPath));
_DllGetClassObject = Win32NativeMethods.GetProcAddress(_redemptionDllHandle,
"DllGetClassObject");
if (_DllGetClassObject.Equals(IntPtr.Zero))
throw new
Exception("Could
not retrieve a pointer to the 'DllGetClassObject' function exported by the dll");
}
DllGetClassObject dllGetClassObject =
(DllGetClassObject)Marshal.GetDelegateForFunctionPointer(_DllGetClassObject,
typeof(DllGetClassObject));
Object unk;
int hr = dllGetClassObject(ref
guid, ref IID_IClassFactory,
out unk);
if (hr != 0)
throw new
Exception("DllGetClassObject failed");
ClassFactory = unk as
IClassFactory;
ClassFactory.CreateInstance(null,
ref IID_IUnknown, out
res);
return (res as
IUnknown);
}
#endregion
}
public class
RedemptionException :
Exception
{
}
}