Adding and removing websites to/from security zones programmatically (C#)

When you're searching binging for how to add or remove websites to/from IE's security zones you'll find tons of samples all showing how to achieve this by creating and deleting registry keys. While doing so will probably work just fine in most scenarios, there is no guarantee that it always will. As the MSDN document About URL Security Zones states, "you should not directly manipulate the registry because information stored in the registry might not always be stored in the same location", which is another way of saying that these keys may or may not change in future versions of Windows (or even IE). So what are you supposed to do instead?

The good news is that there is an interface that, when implemented, allows us to do what we want to do and better yet there is an implementation in urlmon.dll which ships which Internet Explorer and thus with Windows. The bad news though is that this interface is a COM interface. That said, you will need the Windows SDK. Since the SDK does not contain a type library for the types (used) in urlmon.dll (if there is one I wasn't able to find it) and the file urlmon.idl doesn't contain a library statement we need to do the following before we can call the required methods from managed code:

  1. Create an IDL file
  2. Generate a type library from the IDL file
  3. Generate an interop assembly from the type library

A bare minimum IDL file must contain the definitions for the interfaces IInternetSecurityMgrSite and IInternetSecurityManager as well as the aforementioned library statement. I've attached the file urlmon.idl to this post as an example which is basically an extremely stripped-down version of the urlmon.idl that ships with the Windows SDK. Once you have the IDL file, generating the type library and interop assembly is simply a matter of running MIDL and tlbimp.

S:\>midl UrlMon.Idl

Microsoft (R) 32b/64b MIDL Compiler Version 7.00.0500

Copyright (c) Microsoft Corporation 1991-2006. All rights reserved.

Processing .\UrlMon.Idl

UrlMon.Idl

Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\oaidl.idl

oaidl.idl

Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\objidl.idl

objidl.idl

Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\unknwn.idl

unknwn.idl

Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\wtypes.idl

wtypes.idl

Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\basetsd.h

basetsd.h

Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\guiddef.h

guiddef.h

Processing C:\Program Files\Microsoft SDKs\Windows\v6.0A\include\oaidl.acf

oaidl.acf

S:\>tlbimp UrlMon.tlb

Microsoft (R) .NET Framework Type Library to Assembly Converter 3.5.30729.1

Copyright (C) Microsoft Corporation. All rights reserved.

TlbImp : warning TI0000 : At least one of the arguments for 'IInternetSecurityManager.QueryCustomPolicy' cannot be marshaled by the runtime marshaler. Such arguments will therefore be passed as a pointer and may require unsafe code to manipulate.

TlbImp : warning TI0000 : At least one of the arguments for 'IInternetSecurityMgrSite.GetWindow' cannot be marshaled by the runtime marshaler. Such arguments will therefore be passed as a pointer and may require unsafe code to manipulate.

Type library imported to UrlMonTypeLib.dll

Time to switch to C#. After adding a reference to the interop assembly we just created we need to define a couple of constants required for instantiation of and interacting with the Internet Security Manager:

private const string CLSID_InternetSecurityManager = "7b8a2d94-0ac9-11d1-896c-00c04fb6bfc4";

private const int E_FAIL = unchecked((int)0x80004005);

private const int ERROR_FILE_EXISTS = unchecked((int)0x80070050);

private const uint SZM_CREATE = 0;

private const uint SZM_DELETE = 1;

Next, we'll add some of the values from the enumeration URLZONE which represent the well-known zones:

public const uint ZoneLocalMachine = 0;

public const uint ZoneIntranet = 1;

public const uint ZoneTrusted = 2;

public const uint ZoneInternet = 3;

public const uint ZoneUntrusted = 4;

Now we have everything we need to instantiate an Internet Security Manager and call SetZoneMapping(), the method that lets us add/remove sites to/from security zones.

private static IInternetSecurityManager CreateInternetSecurityManager()

{

    Type iismType = Type.GetTypeFromCLSID(new Guid(CLSID_InternetSecurityManager));

    return (IInternetSecurityManager)Activator.CreateInstance(iismType);

}

public static void AddSiteToZone(uint zone, string pattern)

{

    try

    {

        IInternetSecurityManager ism = CreateInternetSecurityManager();

        ism.SetZoneMapping(zone, pattern, SZM_CREATE);

    }

    catch (COMException e)

    {

        if (e.ErrorCode == E_FAIL || e.ErrorCode == ERROR_FILE_EXISTS)

    throw new InvalidOperationException("URL has already been added to a zone", e);

        else

            throw;

    }

    catch (UnauthorizedAccessException e)

    {

        throw new InvalidOperationException("Can't add non-SSL site to zone that requires SSL", e);

    }

}

As I mentioned, SetZoneMapping() is used for both adding and removing sites. However, since we need to know the zone a site is currently in when removing it we need to call MapUrlToZone() first in order to determine the current zone.

public static void RemoveSiteFromZone(string pattern)

{

    uint currentZone;

    IInternetSecurityManager ism = CreateInternetSecurityManager();

    ism.MapUrlToZone(pattern, out currentZone, 0);

    ism.SetZoneMapping(currentZone, pattern, SZM_DELETE);

}

Finally, please read Adding Sites to the Enhanced Security Configuration Zones  for some general hints on dealing with the Trusted zone as well as what flag you need to set for adding a site to the Trusted zone when Enhanced Security Configuration is turned on.


This posting is provided "AS IS" with no warranties, and confers no rights.

UrlMon.Idl