InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

Push notifications with WCF – Security considerations

This is the third article about the .NET component I published at CodePlex recently. To all related articles, click here.

The PushNotificationListener I created for that component uses a WCF endpoint to receive notifications. This means that it needs to open an TCP endpoint of some sort. Thanks to WCF, all the hard stuff is done by the WCF infrastructure. There are, however, two issues which must be resolved before you can receive notifications from your Exchange Server: The Windows Firewall, which blocks all incoming traffic by default and the WCF permission system. To deal with the former, you need to setup an exception for a specific port or for your executable. If you are using Windows Installer Xml, you can use the Firewall Extension to create such an exception during the setup of your application. If you want to do this directly from your application, you will need administrative rights and perform some interop stuff (see the links at the end of the article).

To open an WCF endpoint, you need administrative permissions too, by default. Because this is a very nasty requirement, the system allows you to create so-called reservations which can be tied to specific users. You can use nethsh.exe to manipulate these permissions. You can also use the httpcfg.exe tool. Or, you save you the headache that comes with these tools and head over to Paul Wheelers blog and take a look at this blog post of him: AddressAccessDeniedException: HTTP could not register URL http://+:8080/<…>. He has published a small tool (full source included) to enumerate and manage WCF port reservations.

Here is a screenshot of the reservations on my machine:

image

The really only real interesting endpoint here is the second from the bottom: http://+:80/Temporary_Listen_Addresses. This is a reservation in the form of an UrlPrefix String (more about that here on MSDN) that can be used by everyone (and Dominick Baier on leastprivilege.com has a nice post about the security ramifications here). However, the reservation is there and we can use it for the PushNotificationListener. This is the reason why the default port is 80 and the RelativePath property is set to a folder below the Temporary_Listen_Addresses. If you need to use another relative path or even another port, you’ll have to create a reservation for it. You can take a look at the source code of the HttpNamespaceManager tool from Paul Wheelers Blog on how to do this. Again, this is probably best done during setup. Again, if you are using Windows Installer Xml, you can use a managed custom action (using the DTF Framework) to create the necessary reservation during the setup of your application.

Controlling the Windows Firewall with C#

Here are a few links I just found on the internet. I have tested none of them, but they might give you a hint:

http://www.shafqatahmed.com/2008/01/controlling-win.html

http://www.codeproject.com/KB/winsdk/WinXPSP2Firewall.aspx


Posted by Henning Krause on Thursday, January 1, 2009 9:24 PM, last modified on Thursday, January 1, 2009 9:24 PM
Permalink | Post RSSRSS comment feed

Simple push notification client

As I wrote in my earlier post, I recently published a component on CodePlex that simplifies using push notifications in your applications. Since I’ve not published a full fledged sample application, I will start with a series of blog posts. I will tag all related articles on this topic with “push notifications”, and you can get a list of all articles with this link: http://www.infinitec.de/?tag=/push+notifications.

I’ll start with a very simple application: A console application that creates one subscription get notifications about new mails arriving in the mailbox of the current user.

   1: using System;
   2: using System.Net;
   3: using InfiniTec.Exchange.Notifications;
   4:  
   5: namespace ExchangeNotificationTestClient
   6: {
   7:     internal class Program
   8:     {
   9:         private static void Main()
  10:         {
  11:             // Ignore any certificate errors
  12:             ServicePointManager.ServerCertificateValidationCallback += 
  13:                 (sender, certificate, chain, sslPolicyErrors) => true;
  14:  
  15:             // Setup the adapter which will be used to call into the Exchange WebService
  16:             var adapter = new ExchangeWebServicesAdapter(
  17:                 new Uri("https://casserver/ews/exchange.asmx"), 
  18:                 new NetworkCredential("administrator", "password", "contoso"));
  19:  
  20:             // Create a new subscription collection to manage all the subscriptions
  21:             var subscriptionCollection = new SubscriptionCollection(adapter);
  22:  
  23:             // Setup a listener that listens on port 80 on the local computer
  24:             using (var listener = new PushNotificationListener())
  25:             {
  26:                 // Register for a NewMail notification on the inbox of the administrator
  27:                 subscriptionCollection.Add(
  28:                     new[] {new FolderReference(WellKnownFolderId.Inbox)}, 
  29:                     EventTypes.NewMail);
  30:                 Console.Out.WriteLine("Starting Notification Service...");
  31:                 listener.Start();
  32:  
  33:                 Console.Out.WriteLine("Creating subscription");
  34:  
  35:                 foreach (var subscription in subscriptionCollection)
  36:                 {
  37:                     // Write a line to the console for each new mail received
  38:                     subscription.NewMail += (sender, e) => 
  39:                         Console.Out.WriteLine(string.Format("{0}: New Mail arrived in your inbox", e.Timestamp));
  40:                     subscription.Start(listener);
  41:                 }
  42:  
  43:                 Console.Out.WriteLine("Waiting for notifications... Hit [Enter] to quit...");
  44:  
  45:                 Console.ReadLine();
  46:             }
  47:         }
  48:     }
  49: }

There are four important classes used in this example:

  1. The ExchangeServiceAdapter (created in line 16) is used to actually perform the Web Services calls to the Exchange Server (CAS Role). It’s a rather simple implementation, as it does not support AutoDiscover. You have to specify the Exchange server manually. It does, however, support Exchange Impersonation. And if you want to subscribe to events on public folders, you’ll have to enable this feature by setting the ExchangeServiceAdapter.IsPublicFolderAccessEnabled to true. Your Exchange Server must have Service Pack 1 installed to use this feature.
  2. The PushNotificationListener (created in line 24): This class does all the necessary WCF plumbing to setup a host, receives the notifications and channels them to the right subscription.
  3. The SubscriptionCollection: (created in line 21) It’s not really necessary, but it makes is more easy to handle multiple subscriptions.
  4. The Subscription (created in line 27). You can either create a subscription by calling SubscriptionCollection.Add(), or create a subscription directly via “new Subscription()”.

Both, the PushNotificationListener and the Subscription need to be started to do some actual work. And that’s all you need to do to get a simple notification client up and running. However, there are some security settings to consider, because WCF doesn’t let you run around and open endpoints on your users machines. Additionally, the Windows Firewall needs to be configured correctly to let the notifications through. I’ll discuss this in a separate post.


Posted by Henning Krause on Tuesday, December 30, 2008 10:55 PM, last modified on Saturday, November 27, 2010 6:04 AM
Permalink | Post RSSRSS comment feed

Exchange 2007 Push notifications made easy

Exchange 2007 de-emphasized Exchange Event Sinks in favor of a new notification system which allows an application to receive notifications about changes made to items on a public folder or mailbox folder. There are two types of notifications: Pull and Push. With pull notifications, the calling application is responsible to get changes back from the server (it has to poll the CAS role for changes). Push notifications on the other side use Web Services to call into an external application. There are a restrictions where this does not work (for example firewalls blocking traffic), but otherwise they work really well.

Compared to the WebDAV notifications that where available in earlier versions of Exchange, they have the advantage that the external application gets specific information about the event: Which item has been modified, created, deleted?

However, it’s rather complicated to completely implement a listener for these notifications. Therefore, I have created a wrapper which uses WCF to listen for incoming notifications.

The whole package is available from CodePlex (http://www.codeplex.com/exchangenotification), and there is a list of features. Only the source and a small help file for now, but I will add samples over time.

If you have feedback, please use CodePlex Discussions or send me a mail by using the contact form.


Posted by Henning Krause on Tuesday, December 30, 2008 1:51 PM, last modified on Monday, November 29, 2010 6:03 PM
Permalink | Post RSSRSS comment feed

Exchange Developer Roadmap - WebDAV and StoreEvents gone

Microsoft just released a post about the technologies being removed from the next version of Exchange. I'm ok with WebDAV being removed, given that EWS will be extended to support access to hidden messages and providing strong-typed access to mailbox settings. I can also live with the fact that CdoEx and ExOleDB will be remove. But store events are another thing.

Sure, they are not really easy to implement and the whole access using ADO is a terrible mess. CdoEx makes it not exactly better as there is no support for tasks.

The proposed replacement for store event sinks are transport agents and EWS notification. While transport agents are fine when dealing with email messages, they are useless with respect to appointments, contacts and tasks. This leaves the EWS notifications as the sole option here. Why is this bad?

  • Synchronous execution (access to old data as well): Synchronous event sinks (OnSyncSave, OnSyncDelete) provide direct access to the item being modified (the record is directly available). And during the Begin phase, the event sink can even open the old record and execute actions based on how fields where changed. This feature will be lost completely with EWS notifications.
  • Register once, even for new users (storewide eventsinks): Store wide event sinks are registered once on a store and it will be triggered for every mailbox in the store - even new users. EWS notifications must be registered for each mailbox and the application receiving the mails is required to monitor Exchange for new mailboxes.
  • Access to all properties, even during deletion of an object: With a synchronous OnSyncDelete event sink, all properties of an item can be examined before it gets deleted. With notifications I merely get a notification that and item with a specific ItemId has been deleted. The client application is responsible to track the deleted item - whether is was soft-deleted or moved to the recycle bin. The properties can then be accessed from there. But if the item was hard-deleted (in case of the dumpster being disabled on public folders, for example), one is out of luck. The real problem is this: The ItemId is based on the email address of the mailbox owner as well as the MAPI EntryId of the item (see Exchange 2007 SP1 Item ids). Both, the mailbox address as well as the MAPI entry id are not guaranteed to be stable (Since Exchange 2003 SP2, Exchange recreates meeting items under certain circumstances: CDOEX Calendaring Differences Between Exchange 2003 and Exchange 2003 SP2). This has the effect that the ItemId is not suitable as a long term identifier which should be stored in a database. In scenarios where Exchange data is being replicated to a relational databases, this can become a problem.
  • Order of execution: Synchronous event sinks are called in the order the items were changed. With asynchronous notifications, this cannot be guaranteed.
    To clarify this: The order in which notifications are sent to the client application cannot guaranteed to reflect the order in which the changes were made. To work around this issue, the each notification carries a watermark and a previous watermark. This way a client application can restore the correct order. But with synchronous store events, this comes for free.
  • Silent updates: Due to the possibility to modify an item while it's being changed, the synchronous store events allow some sort of silent update. This works like this:
    1. Along with all the other modifications, a client program sets a special user defined field to an arbitrary value.
    2. The event sink checks this field during the Begin phase, and if it's set it won't process the item. Instead it just removes the property from the element.
  • Modify changes made by a user / Block modifications: A synchronous event sink can modify changes while they are being saved. It can even abort the change, thus preventing a user from deleting an item for example.
    Note: Canceling updates/deletions can break synchronization with ActiveSync or Outlook. So don't do this!
  • Performance: In most scenarios, the WebService receiving the push notifications will not reside on the Exchange server (I know a bunch of administrators who even hesitate to install the .NET Framework on the Exchange server let alone software which does not come from MS*). In this case, the push notifications work like this:
    image
    This makes three network hops to get the properties of a changed item. What makes things worse is the fact that the notifications are send for each modified element. With store event sinks, one could specify a filter so that the sink was only triggered for a subset of elements.

So, while store event sinks are certainly no unconfined pleasure to use, they are far more suited for certain scenarios. I would rather see them being replaced by a managed solution (like transport agents) than the stuff that is coming now.

By the way, the OnTimer, OnMdbShutdown, OnMdbStartup event sinks are being removed without any replacement.

Enough ranting for one day...

* Now, one could argue that a store event sink has far more impact on the Exchange server and those administrators would also hesitate to install them... while that is technically true, there is no other option yet, so they have to swallow that pill.


Posted by Henning Krause on Friday, May 23, 2008 4:41 PM, last modified on Tuesday, July 19, 2011 8:58 AM
Permalink | Post RSSRSS comment feed

ExOleDB problems on Exchange 2007

If you are using the ExOleDB provider on Exchange 2007 it might fail with this error message:

Error : Provider cannot be found. it may not be properly installed. / 800A0E7A / ADODB.Connection

I know of two errors which can cause this message:

  • Your are trying to access the ExOleDB provider from a 32bit process. Exchange 2007 is completely 64bit. This typically breaks old Visual Basic programs or scripts running under the 32bit version of the Windows Scripting Host. To check this, open the Task manager and search for the process name under the "Processes" tab. If the process name is appended with a *32, it runs as a 32bit process.
  • The COM registration of the ExOleDB provider is messed up. This can be fixed by calling Regsvr32 "C:\Program Files\Exchsrvr\bin\exodbprx.dll".

Posted by Henning Krause on Monday, May 19, 2008 4:19 PM, last modified on Tuesday, July 26, 2011 9:56 PM
Permalink | Post RSSRSS comment feed

Exchange 2007 SP1 Item ids

Exchange 2007 introduced a new URL format (Constructing OWA 2007 item ids from WebDAV items) which contained an arbitrary item id, which was based on the EntryId of the item. The format was this:

Length Meaning
1 Length of the structure
sizeof(EntryId) EntryId
1 Item type

This format changed with Exchange Service Pack 1. The layout is now this:

Length Meaning
4 Length of the user's email address
sizeof(EmailAddress) Email address specifying the mailbox which contains the item
4 Size of the EntryId
sizeof(EntryId) The EntryId of the item

This layout also applies to folder ids within a mailbox.

 

   1: <UIRefId="WixUI_Minimal"/>
   2:         <BinaryId="ManagedCustomAction"SourceFile="Include\ManagedCustomAction.dll" />
   3:         <ManagedCustomActionId="test"BinaryKey="ManagedCustomAction"Type="ManagedCustomAction.CustomAction"Execute="immediate"xmlns="http://schemas.infinitec.de/wix/MCAExtension" />
   4:         <ManagedActionSequencexmlns="http://schemas.infinitec.de/wix/MCAExtension">
   5:         <ManagedAction="test"After="CostFinalize"SequenceTable="InstallUISequence" />
   6:     </ManagedActionSequence>
   7: </Product>

Posted by Henning Krause on Thursday, May 15, 2008 6:42 PM, last modified on Thursday, May 15, 2008 8:08 PM
Permalink | Post RSSRSS comment feed

Exchange 2007 store event sinks registration woes

In this article, I wrote about how to write managed event sinks for Exchange 2003 and how to register them with the regsvcs tool on the Exchange server. Normally this goes like this:

    1 c:\Windows\Microsoft.NET\Framework\v2.0.50727\RegSvcs.exe -fc ManagedEventSink.dll

This command will register the necessary COM types and create a new COM+ application on the server. If you try this on a 64-bit Exchange 2007, you'll encounter an error like this:

Microsoft (R) .NET Framework Services Installation Utility Version 2.0.50727.42
Copyright (c) Microsoft Corporation.  All rights reserved.

Type library exporter warning: Referenced type is defined in managed component, which is imported from a type library that could not be loaded because it was not registered (type: 'Microsoft.Interop.Exoledb.IExStoreSyncEvents'; component: 'C:\EventSink\Microsoft.Interop.Exoledb.dll').
Type library exporter warning: Referenced type is defined in managed component, which is imported from a type library that could not be loaded because it was not registered (type: 'Microsoft.Interop.Exoledb.ICreateRegistration'; component: 'C:\EventSink\Microsoft.Interop.Exoledb.dll').
Installed Assembly:
        Assembly: C:\EventSink\ManagedEventSink.dll
        Application: Managed EventSink Example
        TypeLib: C:\EventSink\ManagedEventSink.tlb

This error occurs because Exchange 2007 is a 64bit application, and so is the ExOleDb provider which is used for store event sinks. To create the COM+ application, you should call the RegSvcs tool from the 64bit framework directory, which is located under C:\Windows\Microsoft.NET\Framework64\v2.0.50727.

Once you have created the COM+ application, you can proceed normally by configuring it (as described here and here) and register the eventsink in the store.

Storewide event sinks

To register a store-wide event sink, the GUID of the mailbox store is needed for the registration. In Exchange 2000/2003, this info was most easily obtained from Active Directory Users and Computers:

  1. Open AD User & Computers
  2. Enable the “Advanced Features” item In the “View" Menu
  3. Navigate to the hidden folder “Microsoft Exchange System Objects”
  4. Each mailbox store has a SystemMailbox object associated with it in this folder. Select the mailbox from the store you want to install the sink on. The name of the System Mailbox contains the relevant GUID for the registration.
  5. Copy the GUID from the name of the mailbox to the clipboard.

Although Exchange 2007 lacks the AD Users & Computers integration, this still works; albeit the fancy icons are gone, as shown in this example:

image

But with Exchange 2007, there is another option to quickly get the GUID: PowerShell. Just open the Exchange Management Shell and enter this command:

   1: Get-MailboxDatabase | Select-Object -Property StorageGroupName,Guid

This will return a list of all mailbox stores and their corresponding GUIDs.

The event can now be registered on the store of choice:

   1: regevent.cmd add "onsyncsave;onsyncdelete" EventSink.ProgId file://./backofficestorage/ADMIN/%primarysmtpdomain%/MBX/SystemMailbox%mailboxguid%/StoreEvents/GlobalEvents/EventSinkName.evt -m ANY -f "WHERE $DAV:ishidden$ = false AND $DAV:isfolder$ = false"

Replace the EventSink.ProgId with the ProgId of your sink, the %primarysmtpdomain% with your primary smtp domain and the %mailboxguid% with the GUID of the store you want to register the sink on.


Posted by Henning Krause on Monday, August 20, 2007 12:00 AM, last modified on Wednesday, June 11, 2008 7:42 AM
Permalink | Post RSSRSS comment feed