May 2004 - Posts

Virtual Server and COM Security

The current beta of Microsoft Virtual Server 2005 includes some changes to the API when compared to the original alpha build that had been around for quite a while and was effectively a Microsoft-labeled Connectix build. Besides for some changes to the interface and class names to improve consistency, the most noticeable change to the API is in the security requirements.

Virtual Server is exposed to programmers as a local or remote COM server, hosted in a Windows service. To create an instance of the VMVirtualServer coclass you can use the following pretty typical C++ code. Assume CheckError is a function that checks for a successful HRESULT return code and throws an exception if it is not.

COSERVERINFO serverInfo = { 0 };
serverInfo.pwszName = L"server";

MULTI_QI multiQi = { &__uuidof(IVMVirtualServer) };

CheckError(::CoCreateInstanceEx(__uuidof(VMVirtualServer),
                                0,
                                CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
                                &serverInfo,
                                1,
                                &multiQi));

CComPtr<IVMVirtualServer> virtualServer;
virtualServer.Attach(static_cast<IVMVirtualServer*>(multiQi.pItf));

So far so good. The trouble comes when you want to actually use the API. Here is some code to enumerate the VM’s hosted by Virtual Server.

CComPtr<IVMVirtualMachineCollection> virtualMachines;
CheckError(virtualServer->get_VirtualMachines(&virtualMachines));

long count = 0;
CheckError(virtualMachines->get_Count(&count));

for (long i = 0; i < count; ++i)
{
    CComPtr<IVMVirtualMachine> virtualMachine;

    CheckError(virtualMachines->get_Item(i + 1,
                                         &virtualMachine));

    CComBSTR name;
    CheckError(virtualMachine->get_Name(&name));

    std::wcout << static_cast<PCWSTR>(name) << std::endl;
}

This will compile and might even work depending on how the defaults for DCOM are configured on your computer. This relates to the security changes I mentioned previously. Ignoring the interface name changes, this code would have worked in the original Virtual Server build. The reason it doesn’t work anymore is that Virtual Server’s COM server now requires the ability to impersonate COM clients. If you use Virtual Server’s web administration tool to create a VM and then look at one of the files created for the VM, such as the hard disk image (.VHD) file, you should notice that the creator/owner of the file is your identity as apposed to the identity of the COM server. This indicates that the server is impersonating the COM client in order to access the file system. In previous builds where the server was not impersonating, the files would have been created as the server identity, typically the built-in Network Service account. This sounds great until you realize that the default impersonation level for COM clients allows the server only to get the client’s identity but does not allow it to impersonate the client. This is a good thing as it limits what a malicious or compromised server could do with the client’s identity.

There are a few ways to change the impersonation level, all with different tradeoffs.

The first option is to change the machine-wide default impersonation level. This can be done using the Component Services snap-in, on the Default Properties tab of the property sheet for the My Computer node. This is obviously not what you want to do as it could affect every other COM application running on your computer and leaves them unnecessarily vulnerable to exploits.

The second option is to set the process-wide default impersonation level. This can be done using the CoInitializeSecurity function. There are two problems with this approach. The first is that it has too wide an impact. It affects all the COM clients within the process and also affects any COM servers hosted in the process. The second problem is that you can only call CoInitializeSecurity once per process, so if your Virtual Server code resides in DLL it would not be appropriate for you to call it since you don’t know when you will be loaded.

This leaves one more option and that is to set the impersonation level on every proxy before calling any interface methods through it. You can do this using the CoSetProxyBlanket function. Internally, CoSetProxyBlanket gets the IClientSecurity interface implemented by the proxy and calls the SetBlanket method to set the authentication information that will be used in calls on the proxy. This results in a bit more coding, but far less unnecessary impact on other COM clients that may not want to be exposed to impersonation.

So given the following function to set the impersonation level of a proxy,

void SetImpersonationLevel(IUnknown* proxy)
{
    CheckError(::CoSetProxyBlanket(proxy,
                                   RPC_C_AUTHN_DEFAULT,
                                   RPC_C_AUTHZ_DEFAULT,
                                   COLE_DEFAULT_PRINCIPAL,
                                   RPC_C_AUTHN_LEVEL_DEFAULT,
                                   RPC_C_IMP_LEVEL_IMPERSONATE,
                                   COLE_DEFAULT_AUTHINFO,
                                   EOAC_DEFAULT));
}

you could change the previous example of enumerating the VM’s hosted by Virtual Server to work reliably no matter what the default impersonation level is on the machine or the process.

SetImpersonationLevel(virtualServer);

CComPtr<IVMVirtualMachineCollection> virtualMachines;
CheckError(virtualServer->get_VirtualMachines(&virtualMachines));

SetImpersonationLevel(virtualMachines);

long count = 0;
CheckError(virtualMachines->get_Count(&count));

for (long i = 0; i < count; ++i)
{
    CComPtr<IVMVirtualMachine> virtualMachine;

    CheckError(virtualMachines->get_Item(i + 1,
                                         &virtualMachine));

    SetImpersonationLevel(virtualMachine);

    CComBSTR name;
    CheckError(virtualMachine->get_Name(&name));

    std::wcout << static_cast<PCWSTR>(name) << std::endl;
}


© 2004 Kenny Kerr

 

Posted by KennyKerr with no comments

Boost for Visual C++ Developers

Beman Dawes has written a nice introductory article about the Boost C++ libraries on the Visual C++ Developer Center. Well worth reading if you haven’t played with Boost before.


© 2004 Kenny Kerr

Posted by KennyKerr with 1 comment(s)

Rewards for Parents

Being a parent is hard, but the rewards are huge. One such reward is having my two year old son call me at work while I’m away from my desk and having the message recorded. He hasn’t learnt about voicemail yet.

:)


© 2004 Kenny Kerr

Posted by KennyKerr with no comments

Toward Better Design in Native C++

When you need to write a Windows service application in C++, does it end up looking something like this? If you don’t understand this code, don’t worry we’ll get to that in a minute.

SERVICE_STATUS_HANDLE handle = 0;

SERVICE_STATUS status = { SERVICE_WIN32_OWN_PROCESS,
                          SERVICE_STOPPED,
                          SERVICE_ACCEPT_STOP };

CEvent stopped(true, // manual
               false); // not signaled

void UpdateState(DWORD state)
{
    status.dwCurrentState = state;

    VERIFY(::SetServiceStatus(handle,
                              &status));
}

void WINAPI Handler(DWORD control)
{
    ASSERT(SERVICE_CONTROL_STOP == control);

    UpdateState(SERVICE_STOP_PENDING);

    // Perform shutdown steps here.

    ::WaitForSingleObject(stopped,
                          INFINITE);

    UpdateState(SERVICE_STOPPED);
}

void WINAPI ServiceMain(DWORD,
                        PWSTR*)
{
    handle = ::RegisterServiceCtrlHandler(0,
                                          Handler);
    ASSERT(0 != handle);

    UpdateState(SERVICE_START_PENDING);

    // Perform any startup steps here.

    UpdateState(SERVICE_RUNNING);

    while (SERVICE_RUNNING == status.dwCurrentState)
    {
        // Perform main service function here.
    }

    stopped.Set();
}

int main()
{
    SERVICE_TABLE_ENTRY serviceTable[] =
    {
        { L"", ServiceMain },
        { 0, 0 }
    };

    VERIFY(::StartServiceCtrlDispatcher(serviceTable));
}

I guess the reason so many developers come up with code like this is because many consider the C run-time library and the Win32 API to be the de facto library for C++. Perhaps this is because it has taken so long for true C++ libraries to emerge as standards. Let’s look at the equivalent code in C#.

class EntryPoint
{
    static void Main()
    {
        SampleService service = new SampleService();
        ServiceBase.Run(service);
    }
}

class SampleService : ServiceBase
{
    public SampleService()
    {
        m_stopped = new ManualResetEvent(false);
    }

    protected override void OnStart(string[] args)
    {
        // Perform any startup steps here.

        ThreadPool.QueueUserWorkItem(new WaitCallback(ServiceThread));
    }

    protected override void OnStop()
    {
        // Perform shutdown steps here.

        m_stopping = true;
        m_stopped.WaitOne();
    }

    private void ServiceThread(object state)
    {
        while (!m_stopping)
        {
            // Perform main service function here.
        }

        m_stopped.Set();
    }

    ManualResetEvent m_stopped;
    bool m_stopping;
}

What’s the difference between the C++ and C# examples? The C# example makes use of a library that makes it natural to write a Windows service in C#. The C++ example reverts to procedural C++ akin to C programming. Does that mean that C# is a much more elegant programming language? No. C# is a great language but much of its strength comes from the .NET Framework class library. Is there some magical service that the CLR provides that precludes other environments from exhibiting the same good design in libraries? Certainly not, but the C# language, and many other languages targeting the CLR, have been given a big boost by the availability of a good base class library.

In this piece I plan to use writing a Windows service to illustrate how you can write elegant code in C++ by spending a bit of time thinking about design. Before we get to that let us quickly recap how Windows services work.

Windows Services under the Hood

I’m going to limit this discussion to single-service processes (SERVICE_WIN32_OWN_PROCESS) as it is more familiar to most developers.

When the service control manager (SCM) is asked to start a service, through the StartService function, it starts the process using the CreateProcess function and waits for the process to call the StartServiceCtrlDispatcher function. This function establishes a connection that the SCM can use to send control commands to the service. StartServiceCtrlDispatcher will not return until the service has indicated that it has stopped. Once the connection to the SCM is established, StartServiceCtrlDispatcher creates a secondary thread that is the real starting point for the service. You indicate the address of the thread procedure you wish to use in the SERVICE_TABLE_ENTRY structure passed to StartServiceCtrlDispatcher. This function is typically called ServiceMain. ServiceMain must call RegisterServiceCtrlHandler to register a callback function that the control dispatcher, inside StartServiceCtrlDispatcher, can use to pass control requests to the service. RegisterServiceCtrlHandler also returns a handle that is used in calls to the SetServiceStatus function to update the SCM’s status information about the service.

When the SCM is asked to stop a service, using the SERVICE_CONTROL_STOP control code with the ControlService function, it forward the control request to the control dispatcher which then forwards it to the handler function that was previously registered. The service then indicates that it is stopping by indicating its status as SERVICE_STOP_PENDING using SetServiceStatus. The service then stops what its doing and indicates its status as SERVICE_STOPPED in a final call to SetServiceStatus. At this point, the StartServiceCtrlDispatcher function returns and the main thread unwinds, allowing the process to exit.

In a nutshell, that is how services come to life.

Now onto Design

So about that design. The C++ sample above is problematic because it mixes service-specific functionality with the plumbing needed to communicate with the SCM. It would be a lot more manageable if I could write C++ code like this.

class SampleService : public ServiceBase
{
public:

    SampleService() :
        m_stopping(false)
    {
        // Do nothing
    }

    virtual void Start(DWORD control)
    {
        // Perform any startup steps here.

        ThreadPool::QueueUserWorkItem(ServiceThread,
                                      this);
    }

    virtual void Stop(DWORD control)
    {
        // Perform shutdown steps here.

        m_stopping = true;
        m_stopped.Wait();
    }

private:

    void ServiceThread()
    {
        while (!m_stopping)
        {
            // Perform main service function here.
        }

        m_stopped.Signal();
    }

    ManualResetEvent m_stopped;
    bool m_stopping;

};

int main()
{
    SampleService service;
    ServiceBase::Run(service);
}

Here I am making use of a number of classes that I have written that resemble classes in the .NET Framework. The thing to remember when copying a design to a different programming environment is to be careful not to copy it too literally. Not all concepts translate directly. For example, say you wanted to create a native C++ version of the FileStream class to read and write binary data. It would not make much sense to also create C++ versions of the BinaryReader and BinaryWriter classes with all their ReadXxx methods and Write overloaded methods. The reason is that C++ has a much more natural way of streaming values using operator << and operator >>. Of course if you wanted to support asynchronous I/O, explicit Read and Write methods may well be needed to allow you to pass in an OVERLAPPED structure and wait for completion.

Now back to our service example. The advantage of separating the service plumbing in such a way is evident when you need to be a little creative in the way you run your service. For example, you may want to run the service in a console window for debugging purposes.

Here’s a modified main function that illustrates the possibilities.

int main()
{
    CommandLineParser args;

    if (args.IsFlagPresent(L"debugbreak"))
    {
        ::DebugBreak();
    }

    if (args.IsFlagPresent(L"console"))
    {
        SampleService service;
        service.Start(0);
       
        std::wcout << L"Press the Enter key to stop the service." << std::endl;
        std::wcin.get();

        service.Stop(0);
    }
    else if (args.IsFlagPresent(L"service"))
    {
        SampleService service;
        ServiceBase::Run(service);
    }
}

Well that’s all for today. You can find a full sample service project including a set of classes to help you in building better designed C++ services here. To test the service you’ll need to register it. This can be done using the SC command line tool.

sc create sampleservice binPath= "C:\SampleService\Debug\SampleService.exe /service"

This is just a sample. Don’t expect any support for this code, but I would be happy to hear from you if you found this useful.


© 2004 Kenny Kerr

 

Posted by KennyKerr with 5 comment(s)

Introduction

Welcome! I have been blogging for almost three years on my personal website (http://www.kennyandkarin.com/Kenny/). Trying to maintain a blog on a static HTML page was just proving to be too much of a hassle. I’m looking forward to blogging more frequently now, using Scott Watermasysk’s excellent .Text weblog software.

I spend most of my time designing and building distributed applications for the Microsoft Windows platform. I have a particular passion for C++ and security programming. I love .NET but have fond memories building interesting COM-based systems.

I also love writing and teaching about software. You can find some of my articles on MSDN, the C/C++ User’s Journal and my website.

When I’m not thinking about software I enjoy hanging out with my family.


© 2004 Kenny Kerr

 

Posted by KennyKerr with 2 comment(s)
More Posts