//---------------------------------------------------------------------------- // // 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 }