850 lines
30 KiB
C#

//----------------------------------------------------------------------------
//
// 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;
/// <summary>
/// Implementation for wsman client.
/// </summary>
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;
/// <summary>
/// Implement IDisposable method
/// </summary>
/// <param name="disposing"></param>
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
/// <summary>
/// Constructor
/// </summary>
/// <param name="ip">string, IP address of the Intel(R) AMT machine</param>
/// <param name="username">string, username</param>
/// <param name="password">string, password</param>
/// <param name="secure">bool, true if using a secure tls connection</param>
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();
}
/// <summary>
/// Constructor of the WSMan client using ConnectionInfo
/// </summary>
/// <param name="connection">A class that represents the connection information.</param>
public WinRMWSManClient(ConnectionInfo connection)
{
if (connection == null)
throw new ArgumentNullException("connection");
_Connection = connection;
init();
}
/// <summary>
/// Initialize the client properties
/// </summary>
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();
}
/// <summary>
/// Create a WS-Man session using WinRM
/// </summary>
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;
}*/
/// <summary>
/// Add the flags for start the session
/// </summary>
/// <returns>a number that represent the flags</returns>
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;
}
/// <summary>
/// Set the connection information
/// </summary>
/// <returns>indicate if it success</returns>
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;
}
/// <summary>
/// Check setup for secure connection
/// </summary>
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
/// <summary>
/// Creates a new instance of a resource and returns the URI of the new object.
/// </summary>
/// <param name="resourceUri">Uri, the identifier of the resource to be created</param>
/// <param name="resource">XmlElement, the instance data</param>
/// <returns></returns>
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;
}
}
/// <summary>
/// Delete the resource specified in the resource URI
/// (Only if one instance of the object exists)
/// </summary>
/// <param name="reference">CimReference to delete</param>
public void Delete(CimReference reference)
{
if (reference == null)
throw new ArgumentNullException("reference");
Delete(reference.GetResourceUri(), reference.GetKeys());
}
/// <summary>
/// Delete the resource specified in the resource URI
/// (Only if one instance of the object exists)
/// </summary>
/// <param name="resourceUri">Uri, the identifier of the resource to be deleted</param>
/// <param name="selectors">Selectors to id the object to delete</param>
public void Delete(Uri resourceUri, IEnumerable<Key> 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;
}
}
/// <summary>
/// Retrieves the resource specified by 'resource' and returns an XML representation
/// of the current instance of the resource.
/// </summary>
/// <param name="resource">EndpointReference to delete</param>
/// <returns>XmlElement, an XML of the current instance of the resource. </returns>
public XmlElement Get(CimReference resource)
{
if (resource == null)
throw new ArgumentNullException("resource");
return Get(resource.GetResourceUri(), resource.GetKeys());
}
/// <summary>
/// Retrieves the resource specified by 'resource' and returns an XML representation
/// of the current instance of the resource.
/// </summary>
/// <param name="resourceUri">Uri, the identifier of the resource to be retrieved</param>
/// <param name="selectors">Selectors to id the object to be retrieved</param>
/// <returns>XmlElement, an XML of the current instance of the resource. </returns>
public XmlElement Get(Uri resourceUri, IEnumerable<Key> 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;
}
}
/// <summary>
/// Enumerate resource.
/// </summary>
/// <param name="resourceUri">Uri, The identifier of the resource to be retrived</param>
/// <param name="selectors"></param>
/// <returns>XmlElement array containing the xml response</returns>
public XmlElement[] Enumerate(Uri resourceUri, IEnumerable<Key> selectors)
{
return Enumerate(resourceUri, selectors, null);
}
/// <summary>
/// Enumerate resource.
/// </summary>
/// <param name="resourceUri">Uri, The identifier of the resource to be retrived</param>
/// <param name="selectors">Selectors to id the object to enumerate</param>
/// <param name="options">EnumerateOptions, a struct holds special enum options. Case of a NULL regular enumerate will be execute</param>
/// <returns>XmlElement array containing the xml response</returns>
public XmlElement[] Enumerate(Uri resourceUri, IEnumerable<Key> 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));
}
/// <summary>
/// Invokes a method and returns the results of the method call.
/// </summary>
/// <param name="resourceUri">Uri, The identifier of the resource to be retrived</param>
/// <param name="actionUri">Uri, the action to run</param>
/// <param name="request">The input object for the method </param>
/// <param name="selectors">The selectors of the object to invoke method</param>
/// <returns>Method output</returns>
public XmlElement Invoke(Uri resourceUri, Uri actionUri, XmlElement request, IEnumerable<Key> 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;
}
}
}
/// <summary>
/// Update a resource.
/// </summary>
/// <param name="resourceUri">Uri, the identifier of the resource to update</param>
/// <param name="content"></param>
/// <param name="selectors">Selectors to id the object to update</param>
public void Put(Uri resourceUri, XmlElement content, IEnumerable<Key> 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;
}
}
/// <summary>
/// Method to Identify a machine.
/// </summary>
/// <returns>XML which represent the machine</returns>
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;
}
}
/// <summary>
/// Subscribe to specified event.
/// </summary>
/// <param name="info">Class which contains all data regarding the subscription</param>
/// <returns></returns>
public XmlElement Subscribe(SubscribeInfo info)
{
throw new NotImplementedException();
}
/// <summary>
/// Unsubscribe to specified event.
/// </summary>
/// <param name="selectors">The selectors of the object to Unsubscribe method</param>
public void Unsubscribe(IEnumerable<Key> selectors)
{
throw new NotImplementedException();
}
public XmlElement Subscribe(SubscribeInfo info, IssuedTokens issuedTokens)
{
throw new NotImplementedException();
}
#endregion
#region WSMAN_UTILITIES
/// <summary>
/// Return XML Element that contains the selectors
/// </summary>
/// <param name="selectors">The selectors to add</param>
/// <returns></returns>
private XmlElement[] AddSelectors(IEnumerable<Key> selectors)
{
XmlElement[] Any = null;
if (selectors != null)
{
XmlDocument paramsDoc = null;
bool firstSelector = true;
IEnumerator<Key> 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;
}
/// <summary>
/// Represent the filter for the special enumeration
/// </summary>
/// <param name="options"></param>
/// <param name="selectors"></param>
/// <returns></returns>
private EnumerateRequest getEnumerateRequest(EnumerationOptions options, IEnumerable<Key> 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;
}
/// <summary>
/// Return a resource that contains keys
/// </summary>
/// <param name="resourceUri">The resource URI</param>
/// <param name="selectors">The keys to add</param>
/// <returns></returns>
private IWSManResourceLocator GetResourceLocator(Uri resourceUri, IEnumerable<Key> selectors)
{
IWSManResourceLocator resourceLocator = (IWSManResourceLocator)_WSMan.CreateResourceLocator(resourceUri.AbsoluteUri);
if (selectors != null)
{
IEnumerator<Key> 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;
}
/// <summary>
/// Convert string to XML element
/// </summary>
/// <param name="output">The xml string to convert</param>
/// <returns></returns>
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
}