//----------------------------------------------------------------------------
//
// Copyright (c) Intel Corporation, 2009 - 2014 All Rights Reserved.
//
// File: WinRMWSManClient.cs
//
// Contents: An implementation of a WS-Management client
//
//----------------------------------------------------------------------------
namespace Intel.Manageability.WSManagement
{
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Intel.Manageability.Cim;
using Common.Utils;
using WSManAutomation;
using System.Runtime.InteropServices;
using System.Security;
///
/// Implementation for wsman client.
///
public class WinRMWSManClient : IWSManClient, IDisposable
{
#region DATA_MEMBERS
// WS-Management session
private WSManClass _WSMan = null;
private IWSManSession _Session = null;
//Connection Info
private ConnectionInfo _Connection;
public ConnectionInfo Connection
{
get
{
return _Connection;
}
set
{
_Connection = value;
_credentials = null;
if (!setConnectionInfo())
{
throw new ArgumentException("Problem with credentials");
}
}
}
private CredentialCache _credentials = null;
#endregion DATA_MEMBERS
#region IDisposable Implementation
private bool _disposed = false;
///
/// Implement IDisposable method
///
///
protected virtual void Dispose(bool disposing)
{
if (_disposed)
return;
if (disposing)
{
_Connection?.Dispose();
}
_disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
~WinRMWSManClient()
{
Dispose(false);
}
#endregion
#region CONSTS
private const string WSMAN_ENUM_MODE = "EnumerationMode";
private const string WSMAN_ENUM_EPR = "EnumerateEPR";
private const string WSMAN_ENUM_OBJ_N_EPR = "EnumerateObjectAndEPR";
// WS-Management namespace URI
//private const string DIALECT_NAMESPACE_URI = "http://schemas.dmtf.org/wbem/wsman/1/cimbinding/associationFilter";
private const string ASSOCIATION_FILTER_URI = "http://schemas.dmtf.org/wbem/wsman/1/cimbinding/associationFilter";
private const string SELECTORS_FILTER_URI = "http://schemas.dmtf.org/wbem/wsman/1/wsman/SelectorFilter";
private const string WSMAN_NAMESPACE_URI = "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd";
private const string WSMAN_PREFIX = "wsman";
private const string WSMAN_SELECTOR_SET_FIELD = "SelectorSet";
private const string WSMAN_SELECTOR_FIELD = "Selector";
private const string WSMAN_SELECTOR_NAME = "Name";
#endregion CONSTANTS
#region CONSTRUCTOR_FUNCTIONS
///
/// Constructor
///
/// string, IP address of the Intel(R) AMT machine
/// string, username
/// string, password
/// bool, true if using a secure tls connection
public WinRMWSManClient(String target, String username, SecureString password, bool secure,
bool kerberos, String clientCert, IWebProxy HttpProxy)
{
_Connection = new ConnectionInfo(target, username, password, secure, clientCert, (kerberos ? ConnectionInfo.AuthMethod.Kerberos : ConnectionInfo.AuthMethod.Digest), HttpProxy, null);
init();
}
///
/// Constructor of the WSMan client using ConnectionInfo
///
/// A class that represents the connection information.
public WinRMWSManClient(ConnectionInfo connection)
{
if (connection == null)
throw new ArgumentNullException("connection");
_Connection = connection;
init();
}
///
/// Initialize the client properties
///
private void init()
{
if (!String.IsNullOrEmpty(_Connection.Certificate))
throw new InvalidDataException("Client authentication is not supported by WinRM client.");
if (_Connection.Proxy != null)
throw new InvalidDataException("Proxy is not supported by WinRM client.");
if (!setConnectionInfo())
{
// Throw missing password or user name;
throw new ArgumentException("Problem with credentials");
}
_WSMan = new WSManClass();
CreateSession();
}
///
/// Create a WS-Man session using WinRM
///
private void CreateSession()
{
int iFlags = AddSessionFlags();
IWSManConnectionOptions ConnectionOptions =
(IWSManConnectionOptions)_WSMan.CreateConnectionOptions();
if (_Connection.Auth == ConnectionInfo.AuthMethod.Digest)
{
ConnectionOptions.UserName = _Connection.UserName;
// MSFT WinRM DLL supports string only without SecureString implementation in credentials
ConnectionOptions.Password= _Connection.Password.ConvertToString();
}
String _address = _Connection.HostUri.AbsoluteUri.Replace(_Connection.HostUri.AbsolutePath, "");
_Session = (IWSManSession)_WSMan.CreateSession(_address, iFlags, ConnectionOptions);
}
/*private void Release()
{
System.Runtime.InteropServices.Marshal.ReleaseComObject(_Session);
_Session = null;
}*/
///
/// Add the flags for start the session
///
/// a number that represent the flags
private int AddSessionFlags()
{
// Set session flag for using Digest authentication
int iFlags = _WSMan.SessionFlagUTF8();
// Set session flag for authentication
iFlags += (_Connection.Auth == ConnectionInfo.AuthMethod.Kerberos) ?
// Kerberos Authentication Flags
_WSMan.SessionFlagEnableSPNServerPort() +
_WSMan.SessionFlagUseKerberos() :
// Digest Authentication Flags
_WSMan.SessionFlagCredUsernamePassword() +
_WSMan.SessionFlagUseDigest();
// Add unencryption flag for secure
if (!_Connection.Secure && _Connection.Auth == ConnectionInfo.AuthMethod.Kerberos)
iFlags += _WSMan.SessionFlagNoEncryption();
return iFlags;
}
///
/// Set the connection information
///
/// indicate if it success
private bool setConnectionInfo()
{
if (_Connection.Auth == ConnectionInfo.AuthMethod.Kerberos)
{
SetupAuthManager();
CredentialCache myCache = new CredentialCache();
// Use Kerberos with local credentials
if (_Connection.Password == null || _Connection.Password.Length == 0)
{
if (string.IsNullOrEmpty(_Connection.UserName))
{
myCache.Add(_Connection.HostUri, "Negotiate", System.Net.CredentialCache.DefaultNetworkCredentials);
}
else
throw new ArgumentException("Missing argument for user credentials: password. ");
}
else
{
if (string.IsNullOrEmpty(_Connection.UserName))
{
throw new ArgumentException("Missing argument for user credentials: userName. ");
}
else
{
int i = _Connection.UserName.IndexOf("\\");
if (i > 0)
{
string t_username = _Connection.UserName.Substring(i + 1);
string t_domain = _Connection.UserName.Substring(0, i);
myCache.Add(_Connection.HostUri, "Negotiate", new NetworkCredential(t_username, _Connection.Password.Copy(), t_domain));
}
else
{
myCache.Add(_Connection.HostUri, "Negotiate", new NetworkCredential(_Connection.UserName, _Connection.Password.Copy()));
}
}
}
_credentials = myCache;
return true;
}
if (!string.IsNullOrEmpty(_Connection.UserName) && (_Connection.Password != null && _Connection.Password.Length > 0))
{
CredentialCache myCache = new CredentialCache
{
{
_Connection.HostUri, "Digest",
new NetworkCredential(_Connection.UserName, _Connection.Password.Copy())
}
};
_credentials = myCache;
return true;
}
return false;
}
///
/// Check setup for secure connection
///
private void SetupAuthManager()
{
string hostName;
IPAddress IpAddress;
// Checking if we need to find the host
if (IPAddress.TryParse(_Connection.HostUri.Host, out IpAddress))
{
IPHostEntry hostInfo = Dns.GetHostEntry(IpAddress);
hostName = hostInfo.HostName;
if (hostName == null || hostName.Length == 0)
{
throw new ArgumentNullException("hostName");
}
}
else
{
hostName = _Connection.Host;
}
if (AuthenticationManager.CustomTargetNameDictionary.ContainsKey(_Connection.HostUri.AbsoluteUri) == false)
{
StringBuilder SPN = new StringBuilder("HTTP/" + hostName);
if (_Connection.HostUri.AbsoluteUri.StartsWith("https", StringComparison.CurrentCultureIgnoreCase) == true)
SPN.Append(":16992");
else
SPN.Append(":16993");
AuthenticationManager.CustomTargetNameDictionary.Add(_Connection.HostUri.AbsoluteUri, SPN.ToString());
}
}
#endregion
#region WSMAN_CALLS
///
/// Creates a new instance of a resource and returns the URI of the new object.
///
/// Uri, the identifier of the resource to be created
/// XmlElement, the instance data
///
public CimReference Create(Uri resourceUri, XmlElement resource)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
if (resource == null)
throw new ArgumentNullException("resource");
/*if (_Session != null)
{
Release();
}
CreateSession();*/
try
{
string output = _Session.Create(resourceUri.AbsoluteUri, resource.OwnerDocument.InnerXml, 0);
// Convert the output xml string to CimReference
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(output);
CimReference retValue = new CimReference(xDoc.DocumentElement.Name, xDoc.DocumentElement.NamespaceURI, xDoc.DocumentElement.InnerXml);
return retValue;
}
catch (System.Runtime.InteropServices.COMException e)
{
throw new WinRMException(e).InnerException;
}
}
///
/// Delete the resource specified in the resource URI
/// (Only if one instance of the object exists)
///
/// CimReference to delete
public void Delete(CimReference reference)
{
if (reference == null)
throw new ArgumentNullException("reference");
Delete(reference.GetResourceUri(), reference.GetKeys());
}
///
/// Delete the resource specified in the resource URI
/// (Only if one instance of the object exists)
///
/// Uri, the identifier of the resource to be deleted
/// Selectors to id the object to delete
public void Delete(Uri resourceUri, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
/*if (_Session != null)
{
Release();
}
CreateSession();*/
IWSManResourceLocator resourceLocator = GetResourceLocator(resourceUri, selectors);
try
{
_Session.Delete(resourceLocator, 0);
}
catch (System.Runtime.InteropServices.COMException e)
{
throw new WinRMException(e).InnerException;
}
}
///
/// Retrieves the resource specified by 'resource' and returns an XML representation
/// of the current instance of the resource.
///
/// EndpointReference to delete
/// XmlElement, an XML of the current instance of the resource.
public XmlElement Get(CimReference resource)
{
if (resource == null)
throw new ArgumentNullException("resource");
return Get(resource.GetResourceUri(), resource.GetKeys());
}
///
/// Retrieves the resource specified by 'resource' and returns an XML representation
/// of the current instance of the resource.
///
/// Uri, the identifier of the resource to be retrieved
/// Selectors to id the object to be retrieved
/// XmlElement, an XML of the current instance of the resource.
public XmlElement Get(Uri resourceUri, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
/*if (_Session != null)
{
Release();
}
CreateSession();*/
IWSManResourceLocator resourceLocator = GetResourceLocator(resourceUri, selectors);
try
{
string output = _Session.Get(resourceLocator, 0);
return GetXmlElement(output);
}
catch (System.Runtime.InteropServices.COMException e)
{
throw new WinRMException(e).InnerException;
}
}
///
/// Enumerate resource.
///
/// Uri, The identifier of the resource to be retrived
///
/// XmlElement array containing the xml response
public XmlElement[] Enumerate(Uri resourceUri, IEnumerable selectors)
{
return Enumerate(resourceUri, selectors, null);
}
///
/// Enumerate resource.
///
/// Uri, The identifier of the resource to be retrived
/// Selectors to id the object to enumerate
/// EnumerateOptions, a struct holds special enum options. Case of a NULL regular enumerate will be execute
/// XmlElement array containing the xml response
public XmlElement[] Enumerate(Uri resourceUri, IEnumerable selectors, EnumerationOptions options)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
/*if (_Session != null)
{
Release();
}
CreateSession();*/
ArrayList container = new ArrayList();
try
{
// Define the BODY of the Enum call
EnumerateRequest enumerateReq = getEnumerateRequest(options, selectors);
// Enumerate resource
String filter = (enumerateReq.filter == null) ? String.Empty : (enumerateReq.filter.InnerXml);
String dialect = (options == null) ? SELECTORS_FILTER_URI : ASSOCIATION_FILTER_URI;//(String.IsNullOrEmpty(filter)) ? String.Empty : DIALECT_NAMESPACE_URI;
// Flag - describe the enumeration type: object / reference / object & reference
int flag = (options == null) ? 0 : (int)options.EnumMode;
IWSManEnumerator EnumObj =
(IWSManEnumerator)_Session.Enumerate(resourceUri.AbsoluteUri, filter, dialect, flag);
while (!EnumObj.AtEndOfStream)
{
container.Add(EnumObj.ReadItem());
}
System.Runtime.InteropServices.Marshal.ReleaseComObject(EnumObj);
}
catch (System.Runtime.InteropServices.COMException e)
{
throw new WinRMException(e).InnerException;
}
XmlDocument xDoc = new XmlDocument();
ArrayList elements = new ArrayList();
foreach (String str in container.ToArray(typeof(String)))
{
xDoc.LoadXml(str);
elements.Add(xDoc.DocumentElement);
}
return (XmlElement[])elements.ToArray(typeof(XmlElement));
}
///
/// Invokes a method and returns the results of the method call.
///
/// Uri, The identifier of the resource to be retrived
/// Uri, the action to run
/// The input object for the method
/// The selectors of the object to invoke method
/// Method output
public XmlElement Invoke(Uri resourceUri, Uri actionUri, XmlElement request, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
if (actionUri == null)
throw new ArgumentNullException("actionUri");
if (request == null)
throw new ArgumentNullException("request");
/*if (_Session != null)
{
Release();
}
CreateSession();*/
IWSManResourceLocator resourceLocator;
XmlSerializer mySerializer = new XmlSerializer(request.GetType());
using (StringWriter writer = new StringWriter())
{
resourceLocator = GetResourceLocator(resourceUri, selectors);
mySerializer.Serialize(writer, request);
try
{
String output = _Session.Invoke(actionUri.AbsoluteUri, resourceLocator, writer.ToString(), 0);
return GetXmlElement(output);
}
catch (System.Runtime.InteropServices.COMException e)
{
throw new WinRMException(e).InnerException;
}
}
}
///
/// Update a resource.
///
/// Uri, the identifier of the resource to update
///
/// Selectors to id the object to update
public void Put(Uri resourceUri, XmlElement content, IEnumerable selectors)
{
if (resourceUri == null)
throw new ArgumentNullException("resourceUri");
/*if (_Session != null)
{
Release();
}
CreateSession();*/
try
{
IWSManResourceLocator resourceLocator = GetResourceLocator(resourceUri, selectors);
_Session.Put(resourceLocator, content.OwnerDocument.InnerXml, 0);
}
catch (System.Runtime.InteropServices.COMException e)
{
throw new WinRMException(e).InnerException;
}
}
///
/// Method to Identify a machine.
///
/// XML which represent the machine
public XmlElement Identify()
{
/*if (_Session != null)
{
Release();
}
CreateSession();*/
try
{
String output = _Session.Identify(0);
return GetXmlElement(output);
}
catch (System.Runtime.InteropServices.COMException e)
{
throw new WinRMException(e).InnerException;
}
}
///
/// Subscribe to specified event.
///
/// Class which contains all data regarding the subscription
///
public XmlElement Subscribe(SubscribeInfo info)
{
throw new NotImplementedException();
}
///
/// Unsubscribe to specified event.
///
/// The selectors of the object to Unsubscribe method
public void Unsubscribe(IEnumerable selectors)
{
throw new NotImplementedException();
}
public XmlElement Subscribe(SubscribeInfo info, IssuedTokens issuedTokens)
{
throw new NotImplementedException();
}
#endregion
#region WSMAN_UTILITIES
///
/// Return XML Element that contains the selectors
///
/// The selectors to add
///
private XmlElement[] AddSelectors(IEnumerable selectors)
{
XmlElement[] Any = null;
if (selectors != null)
{
XmlDocument paramsDoc = null;
bool firstSelector = true;
IEnumerator enumerator = selectors.GetEnumerator();
while (enumerator.MoveNext())
{
//Create the outer tag only in the first loop
if (firstSelector)
{
Any = new XmlElement[2];
paramsDoc = new XmlDocument();
Any[1] = paramsDoc.CreateElement(WSMAN_PREFIX, WSMAN_SELECTOR_SET_FIELD, WSMAN_NAMESPACE_URI);
firstSelector = false;
}
Key selector = enumerator.Current;
XmlElement newChild = paramsDoc.CreateElement(WSMAN_PREFIX, WSMAN_SELECTOR_FIELD, WSMAN_NAMESPACE_URI);
newChild.SetAttribute(WSMAN_SELECTOR_NAME, selector.Name);
if (selector.cimReference != null)
{
newChild.InnerXml = selector.cimReference.ToString();
}
else if (selector.Value != null)
{
// if Byte Array
if (selector.Value.GetType().FullName == "System.Byte[]")
{
Object temp = selector.Value;
newChild.InnerText = Convert.ToBase64String((Byte[])temp);
}
else
{
newChild.InnerXml = selector.Value;
}
}
Any[1].AppendChild(newChild);
}
}
return Any;
}
///
/// Represent the filter for the special enumeration
///
///
///
///
private EnumerateRequest getEnumerateRequest(EnumerationOptions options, IEnumerable selectors)
{
XmlElement retValue = null;
EnumerateRequest enumerate = new EnumerateRequest();
enumerate.EnumerationMode = null;
enumerate.filter = null;
if (selectors != null)
{
SelectorFilter filter = new SelectorFilter();
filter.Any = AddSelectors(selectors);
filter.Dialect = SELECTORS_FILTER_URI;// "http://schemas.dmtf.org/wbem/wsman/1/wsman/SelectorFilter";
//Serialize the filter to XmlElement
XmlDocument xDoc = new XmlDocument();
XmlSerializer filterSerializer = new XmlSerializer(typeof(SelectorFilter));
using (StringWriter filterWriter = new StringWriter())
{
filterSerializer.Serialize(filterWriter, filter);
xDoc.LoadXml(filterWriter.ToString());
}
retValue = xDoc.DocumentElement;
}
if (options != null)
{
if (options.EnumMode == EnumerationOptions.EnumerationMode.NONE)
{
enumerate.EnumerationMode = null;
}
else if (options.EnumMode == EnumerationOptions.EnumerationMode.ENUMERATION_REFERENCE)
{
enumerate.EnumerationMode = WSMAN_ENUM_EPR;
}
else if (options.EnumMode == EnumerationOptions.EnumerationMode.ENUMERATION_OBJ_AND_REFERENCE)
{
enumerate.EnumerationMode = WSMAN_ENUM_OBJ_N_EPR;
}
if (options.Filter != null)
{
XmlDocument xDoc = new XmlDocument();
XmlSerializer filterSerializer = new XmlSerializer(options.Filter.GetType());
using (StringWriter filterWriter = new StringWriter())
{
Type ttt = options.Filter.GetType();
filterSerializer.Serialize(filterWriter, options.Filter);
xDoc.LoadXml(filterWriter.ToString());
retValue = xDoc.DocumentElement;
}
}
}
enumerate.filter = retValue;
return enumerate;
}
///
/// Return a resource that contains keys
///
/// The resource URI
/// The keys to add
///
private IWSManResourceLocator GetResourceLocator(Uri resourceUri, IEnumerable selectors)
{
IWSManResourceLocator resourceLocator = (IWSManResourceLocator)_WSMan.CreateResourceLocator(resourceUri.AbsoluteUri);
if (selectors != null)
{
IEnumerator enumerator = selectors.GetEnumerator();
while (enumerator.MoveNext())
{
Key selector = enumerator.Current;
if (!String.IsNullOrEmpty(selector.Name) &&
!String.IsNullOrEmpty(selector.Value))
{
resourceLocator.AddSelector(enumerator.Current.Name, enumerator.Current.Value);
}
}
}
return resourceLocator;
}
///
/// Convert string to XML element
///
/// The xml string to convert
///
private XmlElement GetXmlElement(string output)
{
XmlDocument xDoc = new XmlDocument();
xDoc.LoadXml(output);
return xDoc.DocumentElement;
}
#endregion
}
#region WSMAN_SOAP_STRUCT
[System.Xml.Serialization.XmlRoot("Enumerate", Namespace = "http://schemas.xmlsoap.org/ws/2004/09/enumeration")]
public struct EnumerateRequest
{
[System.Xml.Serialization.XmlElement(Namespace = "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd")]
public string EnumerationMode;
[System.Xml.Serialization.XmlAnyElementAttribute()]
public XmlElement filter;
}
#endregion
#region EXCEPTION_CLASS
public class WinRMException : Exception
{
#region ERROR_CONSTS
// Connection Error Exception
private const long WINRM_CONNECTION_ERROR = 0x80338012;
#endregion
#region CONSTRUCTOR
public WinRMException(COMException e)
{
long hexCode = (e.ErrorCode < 0 ? e.ErrorCode + 0x100000000 : e.ErrorCode);
if (hexCode == WINRM_CONNECTION_ERROR)
{
WebException we = new WebException(e.Message, WebExceptionStatus.ConnectFailure);
throw new WSManException(we);
}
else
throw new WSManException(e.Message);
}
#endregion
}
#endregion
}