Establishing WMI Connections

Throughout all of the code examples, shown earlier in this book, I consistently and consciously neglected most of the issues related to WMI security, crudely assuming that these examples are being executed by a user with administrative privileges. Moreover, when I described the process of connecting to WMI, I barely scratched the surface and chose to rely on the ability of System.Management types to automatically establish a connection on an as-needed basis. Thus, all of the code examples used something similar to the following boilerplate code to bind to a WMI class or object:

ManagementObject mo = new ManagementObject(@"\\.\root\CIMV2:Win32_Process=100");
Console.WriteLine(mo["__CLASS"]);
...

This code looks deceivingly simple and you may not realize what is really going on behind the scenes.

Before the newly constructed object can be used, a valid connection to WMI must be established and the instance of the ManagementObject type must be bound to the underlying WMI object. However, the code does not seem to be doing either of these things—instead, it simply instantiates the object and prints out its properties. Such simplicity and ease of use is afforded by the ability of the System.Management ManagementObject type to initialize itself and establish the appropriate connections in a lazy fashion. In fact, none of the type constructors issue any COM API calls, and therefore, they do not even attempt to contact WMI. Instead, when an instance of a type is first accessed, it undergoes the initialization stage. The initialization code typically checks whether the instance is already connected to WMI, and if necessary, it invokes the initialization sequence. As part of the initialization, the object of type ManagementScope is created and then its initialization code is invoked. The ManagementScope's InitializeGuts method, obtains a pointer to IWbemLocator object and calls its ConnectServer method to retrieve the IWbemServices interface pointer. The IWbemLocator::ConnectServer method has the following signature:

HRESULT IWbemLocator::ConnectServer(
const BSTR strNetworkResource,
const BSTR strUser,
const BSTR strPassword,
const BSTR strLocale,
LONG lSecurityFlags,
const BSTR strAuthority,
IWbemContext* pCtx,
IWbemServices** ppNamespace
);

where
• strNetworkResource: A pointer to a valid BSTR that contains a WMI path to the target object.

• strUser: A pointer to a valid BSTR that contains the name of the security principal to be used to establish the connection to WMI. If this parameter is set to NULL, ConnectServer will use the name of the currently logged on user. When using the user name from a domain other than the current domain, the name should be prefixed with the domain name and a double backslash separator—i.e., \\.

• strPassword: A pointer to a valid BSTR that contains a password to be used to establish the connection to WMI. If this parameter is set to NULL, ConnectServer will use the password of the currently logged on user. An empty string is considered to be a valid password of zero length.

• strLocale: A pointer to a valid BSTR that specifies the locale (i.e., MS_xxx) to be used to retrieve the information from WMI. If this parameter is set to NULL, the current locale is used.

• lSecurityFlags: Reserved for future use. Must be set to zero.

• strAuthority: A pointer to a valid BSTR that contains a designation of the authentication method to be used for establishing the connection, as well as the name of the domain in which to obtain the user information for authentication. If this parameter begins with string "Kerberos:", Kerberos authentication is used and the remainder of the string must contain a valid Kerberos principal name. If the parameter begins with "NTLMDOMAIN:", then Windows NT Challenge/Response authentication protocol (also known as Windows NT LAN Manager or NTLM protocol) is used and the remainder of the string must contain a valid NTLM domain name. If the parameter is set to NULL, ConnectServer uses NTLM authentication and the NTLM domain of the currently logged on user. Note that the name of the domain cannot be specified simultaneously in two places (i.e., strUser and strAuthority parameters); doing so will cause ConnectServer to fail.

• pCtx: A pointer to the IWbemContext object used to provide additional information to certain WMI providers. This parameter is typically set to NULL.

• ppNamespace: An output parameter used to receive the pointer to a valid IWbemServices object.

As you can deduce from the method signature, it is possible to control many aspects of the connection establishment process, such as the name of the connecting user and domain as well as the authentication method. Unfortunately, the lazy initialization sequence of the ManagementObject type constructs a default instance of the ManagementScope type so that the IWbemLocator::ConnectServer method is called with most of its parameters set to NULL values. Obviously, this causes WMI to use the NTLM authentication along with the name and domain of the currently logged on user. While adequate for some of the management scenarios, this may result in access-denied errors when the current user is not granted full administrative access to the target WMI resources.

The good news is that you can override the default behavior by explicitly constructing an instance of the ManagementScope type and associating it with the ManagementObject. Take a look at the following code snippet:

ManagementScope ms = new ManagementScope();
ms.Path = new ManagementPath(@"\\BCK_OFFICE\root\CIMV2");
ms.Options.Username = "Administrator";
ms.Options.Password = "password";
ManagementObject mo = new ManagementObject(@" Win32_Process=100");
mo.Scope = ms;
Console.WriteLine(mo["__CLASS"]);

Here, the Path property of the newly created ManagementScope object is assigned to the instance of the ManagementPath type, which specifies the remote WMI namespace to connect to. Then, the Username and Password properties of the ConnectionOptions object, pointed to by the Options property of the ManagementScope object, are initialized to the name of the user and the password to be used for establishing a connection. Finally, the new instance of the ManagementObject type is created and associated with the ManagementScope object by setting its Scope property. When the ManagementObject instance is accessed by the WriteLine method, rather than constructing a default ManagementScope object, the initialization code of the ManagementObject type uses the ManagementScope instance associated with the object. Interestingly, in this case, all information that pertains to the WMI connection—not only the user name and the password, but also the namespace path—is always extracted from the associated ManagementScope object. Thus, the following code is incorrect and will generate an error:

ManagementScope ms = new ManagementScope();
ms.Options.Username = "Administrator";
ms.Options.Password = "password";
ManagementObject mo = new ManagementObject(
@"\\BCK_OFFICE\root\CIMV2:Win32_Process=100");
mo.Scope = ms;
Console.WriteLine(mo["__CLASS"]);

Here, rather than setting the Path property of the ManagementScope object, we supply a full namespace and an object path to the constructor of the ManagementObject type. Therefore, the resulting ManagementScope object contains a default namespace path \\.\root\CIMV2. Upon setting the Scope property of the ManagementObject instance, the namespace path of ManagementObject essentially is overridden by the namespace path of the associated ManagementScope object. Asa result, rather than binding to the remote namespace \\BCK_OFFICE\root\CIMV2, the initialization code of the ManagementObject type binds to the local \\.\root\CIMV2 namespace. For security reasons, it is not allowed to change the user credentials when it connects to a local namespace; therefore, the preceding code throws a ManagementException, complaining that user credentials cannot be used for local connections.

The coding pattern just discussed is not the only way to create a ManagementScope object and associate it with an appropriate System.Management type. One of the constructors of the ManagementObject type, for instance, accepts the instance of ManagementScope as a parameter:

ManagementScope ms = new ManagementScope();
ms.Path = new ManagementPath(@"\\BCK_OFFICE\root\CIMV2");
ms.Options.Username = "Administrator";
ms.Options.Password = "password";
ManagementPath mp = new ManagementPath("Win32_Process=100");
ManagementObject mo = new ManagementObject(ms, mp, null);
Console.WriteLine(mo["__CLASS"]);

Here, the ManagementScope object is the first parameter of the ManagementObject type constructor. The second parameter is an object of type ManagementPath, which points to an appropriate WMI Win32_Process object to bind to. Finally, the last parameter is an instance of ObjectGetOptions type, which, for the purposes of this discussion, may simply be ignored, hence it is set to null. Note that this code behaves similarly to the code snippet, shown earlier—in other words, the namespace path of the ManagementScope instance overrides the namespace path of the ManagementPath object. Thus, if you forget to initialize the Path property of the ManagementScope object, the code will generate an error.

Since the default ManagementScope object is constructed during the initialization of the ManagementObject unless the ManagementScope parameter is supplied to the type constructor, it is entirely possible to use the default object rather than constructing a brand new one:

ManagementObject mo = new ManagementObject(
@"\\BCK_OFFICE\root\CIMV2:Win32_Process=100");
mo.Scope.Options.Username = "Administrator";
mo.Scope.Options.Password = "password";
Console.WriteLine(mo["__CLASS"]);

This code is certainly more concise and, perhaps, a bit more efficient than my first example because it alleviates the need for allocating a new instance of the ManagementScope type.

Finally, you do not need to rely on the initialization code of the ManagementObject type to implicitly establish a connection to WMI. With the ManagementScope, you can request a connection explicitly:

ManagementScope ms = new ManagementScope();
ms.Path = new ManagementPath(@"\\BCK_OFFICE\root\CIMV2");
ms.Options.Username = "Administrator";
ms.Options.Password = "password";
ms.Connect();
ManagementObject mo = new ManagementObject(@" Win32_Process=100");
mo.Scope = ms;
Console.WriteLine(mo["__CLASS"]);

As I already mentioned, the initialization sequence of the ManagementObject type first checks if there is a valid WMI connection and then attempts to use it if such a connection exists. Here, since the ManagementScope object is already connected to WMI by the time the ManagementObject initialization code is invoked, the entire connection establishment process is bypassed and the ManagementScope's connection is used instead. The apparent benefit of such an approach is that you can reuse a single connected ManagementScope object with multiple instances of the ManagementObject, thus achieving a marginal performance gain.

The ManagementObject type is not the only System.Management type designed to work in conjunction with ManagementScope. In fact, all System.Management types that represent WMI entities, such as ManagementClass, ManagementEventWatcher, and ManagementObjectSearcher, offer the same functionality. Thus, each of these types is equipped with a constructor that takes the ManagementScope object as a parameter and the Scope property, which exposes the associated instance of the ManagementScope type.

Source of Information :
Dot NET System Management Services - Apress

0 comments


Subscribe to Developer Techno ?
Enter your email address:

Delivered by FeedBurner