1141 lines
44 KiB
C#
1141 lines
44 KiB
C#
// Copyright (c) Microsoft Corporation.
|
|
// Licensed under the MIT License.
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Globalization;
|
|
using System.IO;
|
|
using System.Management.Automation;
|
|
using System.Management.Automation.Provider;
|
|
using System.Reflection;
|
|
using System.Resources;
|
|
using System.Runtime.InteropServices;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading;
|
|
using System.Xml;
|
|
|
|
using Microsoft.Win32;
|
|
|
|
namespace Microsoft.WSMan.Management
|
|
{
|
|
[SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")]
|
|
internal class WSManHelper
|
|
{
|
|
// regular expressions
|
|
private const string PTRN_URI_LAST = @"([a-z_][-a-z0-9._]*)$";
|
|
private const string PTRN_OPT = @"^-([a-z]+):(.*)";
|
|
private const string PTRN_HASH_TOK = @"\s*([\w:]+)\s*=\s*(\$null|""([^""]*)"")\s*";
|
|
|
|
// schemas
|
|
private const string URI_IPMI = @"http://schemas.dmtf.org/wbem/wscim/1/cim-schema";
|
|
private const string URI_WMI = @"http://schemas.microsoft.com/wbem/wsman/1/wmi";
|
|
private const string NS_IPMI = @"http://schemas.dmtf.org/wbem/wscim/1/cim-schema";
|
|
private const string NS_CIMBASE = @"http://schemas.dmtf.org/wbem/wsman/1/base";
|
|
private const string NS_WSMANL = @"http://schemas.microsoft.com";
|
|
private const string NS_XSI = @"xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance""";
|
|
private const string ATTR_NIL = @"xsi:nil=""true""";
|
|
private const string ATTR_NIL_NAME = @"xsi:nil";
|
|
private const string NS_XSI_URI = @"http://www.w3.org/2001/XMLSchema-instance";
|
|
private const string ALIAS_XPATH = @"xpath";
|
|
private const string URI_XPATH_DIALECT = @"http://www.w3.org/TR/1999/REC-xpath-19991116";
|
|
|
|
// credSSP strings
|
|
internal string CredSSP_RUri = "winrm/config/client/auth";
|
|
internal string CredSSP_XMLNmsp = "http://schemas.microsoft.com/wbem/wsman/1/config/client/auth";
|
|
internal string CredSSP_SNode = "/cfg:Auth/cfg:CredSSP";
|
|
internal string Client_uri = "winrm/config/client";
|
|
internal string urlprefix_node = "/cfg:Client/cfg:URLPrefix";
|
|
internal string Client_XMLNmsp = "http://schemas.microsoft.com/wbem/wsman/1/config/client";
|
|
|
|
internal string Service_Uri = "winrm/config/service";
|
|
internal string Service_UrlPrefix_Node = "/cfg:Service/cfg:URLPrefix";
|
|
internal string Service_XMLNmsp = "http://schemas.microsoft.com/wbem/wsman/1/config/service";
|
|
internal string Service_CredSSP_Uri = "winrm/config/service/auth";
|
|
internal string Service_CredSSP_XMLNmsp = "http://schemas.microsoft.com/wbem/wsman/1/config/service/auth";
|
|
|
|
// gpo registry path and keys
|
|
internal string Registry_Path_Credentials_Delegation = @"SOFTWARE\Policies\Microsoft\Windows";
|
|
internal string Key_Allow_Fresh_Credentials = "AllowFreshCredentials";
|
|
internal string Key_Concatenate_Defaults_AllowFresh = "ConcatenateDefaults_AllowFresh";
|
|
internal string Delegate = "delegate";
|
|
internal string keyAllowcredssp = "AllowCredSSP";
|
|
|
|
// 'Constants for MS-XML
|
|
private const string NODE_ATTRIBUTE = "2";
|
|
private const int NODE_TEXT = 3;
|
|
|
|
// strings for dialects
|
|
internal string ALIAS_WQL = @"wql";
|
|
internal string ALIAS_ASSOCIATION = @"association";
|
|
internal string ALIAS_SELECTOR = @"selector";
|
|
internal string URI_WQL_DIALECT = @"http://schemas.microsoft.com/wbem/wsman/1/WQL";
|
|
internal string URI_SELECTOR_DIALECT = @"http://schemas.dmtf.org/wbem/wsman/1/wsman/SelectorFilter";
|
|
internal string URI_ASSOCIATION_DIALECT = @" http://schemas.dmtf.org/wbem/wsman/1/cimbinding/associationFilter";
|
|
|
|
// string for operation
|
|
internal string WSManOp = null;
|
|
|
|
private readonly PSCmdlet cmdletname;
|
|
private readonly NavigationCmdletProvider _provider;
|
|
|
|
private FileStream _fs;
|
|
private StreamReader _sr;
|
|
|
|
private static readonly ResourceManager _resourceMgr = new ResourceManager("Microsoft.WSMan.Management.resources.WsManResources", typeof(WSManHelper).GetTypeInfo().Assembly);
|
|
|
|
//
|
|
//
|
|
// Below class is just a static container which would release sessions in case this DLL is unloaded.
|
|
internal class Sessions
|
|
{
|
|
/// <summary>
|
|
/// Dictionary object to store the connection.
|
|
/// </summary>
|
|
internal static readonly Dictionary<string, object> SessionObjCache = new Dictionary<string, object>();
|
|
|
|
~Sessions()
|
|
{
|
|
ReleaseSessions();
|
|
}
|
|
}
|
|
|
|
internal static readonly Sessions AutoSession = new Sessions();
|
|
//
|
|
//
|
|
//
|
|
|
|
internal static void ReleaseSessions()
|
|
{
|
|
lock (Sessions.SessionObjCache)
|
|
{
|
|
object sessionobj;
|
|
foreach (string key in Sessions.SessionObjCache.Keys)
|
|
{
|
|
Sessions.SessionObjCache.TryGetValue(key, out sessionobj);
|
|
try
|
|
{
|
|
Marshal.ReleaseComObject(sessionobj);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// Somehow the object was a null reference. Ignore the error
|
|
}
|
|
|
|
sessionobj = null;
|
|
}
|
|
|
|
Sessions.SessionObjCache.Clear();
|
|
}
|
|
}
|
|
|
|
internal WSManHelper()
|
|
{
|
|
}
|
|
|
|
internal WSManHelper(PSCmdlet cmdlet)
|
|
{
|
|
cmdletname = cmdlet;
|
|
}
|
|
|
|
internal WSManHelper(NavigationCmdletProvider provider)
|
|
{
|
|
_provider = provider;
|
|
}
|
|
|
|
internal static void ThrowIfNotAdministrator()
|
|
{
|
|
System.Security.Principal.WindowsIdentity currentIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
|
|
System.Security.Principal.WindowsPrincipal principal = new System.Security.Principal.WindowsPrincipal(currentIdentity);
|
|
if (!principal.IsInRole(System.Security.Principal.WindowsBuiltInRole.Administrator))
|
|
{
|
|
string message = _resourceMgr.GetString("ErrorElevationNeeded");
|
|
throw new InvalidOperationException(message);
|
|
}
|
|
}
|
|
|
|
internal string GetResourceMsgFromResourcetext(string rscname)
|
|
{
|
|
return _resourceMgr.GetString(rscname);
|
|
}
|
|
|
|
internal static string FormatResourceMsgFromResourcetextS(string rscname,
|
|
params object[] args)
|
|
{
|
|
return FormatResourceMsgFromResourcetextS(_resourceMgr, rscname, args);
|
|
}
|
|
|
|
internal string FormatResourceMsgFromResourcetext(string resourceName,
|
|
params object[] args)
|
|
{
|
|
return FormatResourceMsgFromResourcetextS(_resourceMgr, resourceName, args);
|
|
}
|
|
|
|
private static string FormatResourceMsgFromResourcetextS(
|
|
ResourceManager resourceManager,
|
|
string resourceName,
|
|
object[] args)
|
|
{
|
|
if (resourceManager == null)
|
|
{
|
|
throw new ArgumentNullException(nameof(resourceManager));
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(resourceName))
|
|
{
|
|
throw new ArgumentNullException(nameof(resourceName));
|
|
}
|
|
|
|
string template = resourceManager.GetString(resourceName);
|
|
|
|
string result = null;
|
|
if (template != null)
|
|
{
|
|
result = string.Format(CultureInfo.CurrentCulture,
|
|
template, args);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Add a session to dictionary.
|
|
/// </summary>
|
|
/// <param name="key">Connection string.</param>
|
|
/// <param name="value">Session object.</param>
|
|
internal void AddtoDictionary(string key, object value)
|
|
{
|
|
key = key.ToLowerInvariant();
|
|
lock (Sessions.SessionObjCache)
|
|
{
|
|
if (!Sessions.SessionObjCache.ContainsKey(key))
|
|
{
|
|
Sessions.SessionObjCache.Add(key, value);
|
|
}
|
|
else
|
|
{
|
|
object objsession = null;
|
|
Sessions.SessionObjCache.TryGetValue(key, out objsession);
|
|
try
|
|
{
|
|
Marshal.ReleaseComObject(objsession);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// Somehow the object was a null reference. Ignore the error
|
|
}
|
|
|
|
Sessions.SessionObjCache.Remove(key);
|
|
Sessions.SessionObjCache.Add(key, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal object RemoveFromDictionary(string computer)
|
|
{
|
|
object objsession = null;
|
|
computer = computer.ToLowerInvariant();
|
|
lock (Sessions.SessionObjCache)
|
|
{
|
|
if (Sessions.SessionObjCache.ContainsKey(computer))
|
|
{
|
|
Sessions.SessionObjCache.TryGetValue(computer, out objsession);
|
|
try
|
|
{
|
|
Marshal.ReleaseComObject(objsession);
|
|
}
|
|
catch (ArgumentException)
|
|
{
|
|
// Somehow the object was a null reference. Ignore the error
|
|
}
|
|
|
|
Sessions.SessionObjCache.Remove(computer);
|
|
}
|
|
}
|
|
|
|
return objsession;
|
|
}
|
|
|
|
internal static Dictionary<string, object> GetSessionObjCache()
|
|
{
|
|
try
|
|
{
|
|
lock (Sessions.SessionObjCache)
|
|
{
|
|
if (!Sessions.SessionObjCache.ContainsKey("localhost"))
|
|
{
|
|
IWSManEx wsmanObject = (IWSManEx)new WSManClass();
|
|
IWSManSession SessionObj = (IWSManSession)wsmanObject.CreateSession(null, 0, null);
|
|
Sessions.SessionObjCache.Add("localhost", SessionObj);
|
|
}
|
|
}
|
|
}
|
|
catch (IOException)
|
|
{
|
|
}
|
|
catch (System.Security.SecurityException)
|
|
{
|
|
}
|
|
catch (System.UnauthorizedAccessException)
|
|
{
|
|
}
|
|
catch (COMException)
|
|
{
|
|
}
|
|
|
|
return Sessions.SessionObjCache;
|
|
}
|
|
|
|
internal string GetRootNodeName(string operation, string resourceUri, string actionStr)
|
|
{
|
|
string resultStr = null, sfx = null;
|
|
if (resourceUri != null)
|
|
{
|
|
resultStr = resourceUri;
|
|
resultStr = StripParams(resultStr);
|
|
|
|
Regex regexpr = new Regex(PTRN_URI_LAST, RegexOptions.IgnoreCase);
|
|
MatchCollection matches = regexpr.Matches(resultStr);
|
|
if (matches.Count > 0)
|
|
{
|
|
if (operation.Equals("invoke", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
sfx = "_INPUT";
|
|
resultStr = string.Concat(actionStr, sfx);
|
|
}
|
|
else
|
|
{
|
|
resultStr = matches[0].ToString();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// error
|
|
}
|
|
}
|
|
|
|
return resultStr;
|
|
}
|
|
|
|
internal string StripParams(string uri)
|
|
{
|
|
int pos = uri.IndexOf('?');
|
|
if (pos > 0)
|
|
return uri.Substring(pos, uri.Length - pos);
|
|
else
|
|
return uri;
|
|
}
|
|
|
|
internal string ReadFile(string path)
|
|
{
|
|
if (!File.Exists(path))
|
|
{
|
|
throw new ArgumentException(GetResourceMsgFromResourcetext("InvalidFileName"));
|
|
}
|
|
|
|
string strOut = null;
|
|
try
|
|
{
|
|
_fs = new FileStream(path, FileMode.Open, FileAccess.Read);
|
|
// create stream Reader
|
|
_sr = new StreamReader(_fs);
|
|
strOut = _sr.ReadToEnd();
|
|
}
|
|
catch (ArgumentNullException e)
|
|
{
|
|
ErrorRecord er = new ErrorRecord(e, "ArgumentNullException", ErrorCategory.InvalidArgument, null);
|
|
cmdletname.ThrowTerminatingError(er);
|
|
}
|
|
catch (UnauthorizedAccessException e)
|
|
{
|
|
ErrorRecord er = new ErrorRecord(e, "UnauthorizedAccessException", ErrorCategory.PermissionDenied, null);
|
|
cmdletname.ThrowTerminatingError(er);
|
|
}
|
|
catch (FileNotFoundException e)
|
|
{
|
|
ErrorRecord er = new ErrorRecord(e, "FileNotFoundException", ErrorCategory.ObjectNotFound, null);
|
|
cmdletname.ThrowTerminatingError(er);
|
|
}
|
|
catch (DirectoryNotFoundException e)
|
|
{
|
|
ErrorRecord er = new ErrorRecord(e, "DirectoryNotFoundException", ErrorCategory.ObjectNotFound, null);
|
|
cmdletname.ThrowTerminatingError(er);
|
|
}
|
|
catch (System.Security.SecurityException e)
|
|
{
|
|
ErrorRecord er = new ErrorRecord(e, "SecurityException", ErrorCategory.SecurityError, null);
|
|
cmdletname.ThrowTerminatingError(er);
|
|
}
|
|
finally
|
|
{
|
|
if (_sr != null)
|
|
{
|
|
// _sr.Close();
|
|
_sr.Dispose();
|
|
}
|
|
|
|
if (_fs != null)
|
|
{
|
|
// _fs.Close();
|
|
_fs.Dispose();
|
|
}
|
|
}
|
|
|
|
return strOut;
|
|
}
|
|
|
|
internal string ProcessInput(IWSManEx wsman, string filepath, string operation, string root, Hashtable valueset, IWSManResourceLocator resourceUri, IWSManSession sessionObj)
|
|
{
|
|
string resultString = null;
|
|
|
|
// if file path is given
|
|
if (!string.IsNullOrEmpty(filepath) && valueset == null)
|
|
{
|
|
if (!File.Exists(filepath))
|
|
{
|
|
throw new FileNotFoundException(_resourceMgr.GetString("InvalidFileName"));
|
|
}
|
|
|
|
resultString = ReadFile(filepath);
|
|
return resultString;
|
|
}
|
|
|
|
switch (operation)
|
|
{
|
|
case "new":
|
|
case "invoke":
|
|
|
|
string parameters = null, nilns = null;
|
|
string xmlns = GetXmlNs(resourceUri.ResourceUri);
|
|
|
|
// if valueset is given, i.e hashtable
|
|
if (valueset != null)
|
|
{
|
|
foreach (DictionaryEntry entry in valueset)
|
|
{
|
|
parameters = parameters + "<p:" + entry.Key.ToString();
|
|
if (entry.Value.ToString() == null)
|
|
{
|
|
parameters = parameters + " " + ATTR_NIL;
|
|
nilns = " " + NS_XSI;
|
|
}
|
|
|
|
parameters = parameters + ">" + entry.Value.ToString() + "</p:" + entry.Key.ToString() + ">";
|
|
}
|
|
}
|
|
|
|
resultString = "<p:" + root + " " + xmlns + nilns + ">" + parameters + "</p:" + root + ">";
|
|
|
|
break;
|
|
case "set":
|
|
|
|
string getResult = sessionObj.Get(resourceUri, 0);
|
|
XmlDocument xmlfile = new XmlDocument();
|
|
xmlfile.LoadXml(getResult);
|
|
|
|
string xpathString = null;
|
|
if (valueset != null)
|
|
{
|
|
foreach (DictionaryEntry entry in valueset)
|
|
{
|
|
xpathString = @"/*/*[local-name()=""" + entry.Key + @"""]";
|
|
if (entry.Key.ToString().Equals("location", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
// 'Ignore cim:Location
|
|
xpathString = @"/*/*[local-name()=""" + entry.Key + @""" and namespace-uri() != """ + NS_CIMBASE + @"""]";
|
|
}
|
|
|
|
XmlNodeList nodes = xmlfile.SelectNodes(xpathString);
|
|
if (nodes.Count == 0)
|
|
{
|
|
throw new ArgumentException(_resourceMgr.GetString("NoResourceMatch"));
|
|
}
|
|
else if (nodes.Count > 1)
|
|
{
|
|
throw new ArgumentException(_resourceMgr.GetString("MultipleResourceMatch"));
|
|
}
|
|
else
|
|
{
|
|
XmlNode node = nodes[0];
|
|
if (node.HasChildNodes)
|
|
{
|
|
if (node.ChildNodes.Count > 1)
|
|
{
|
|
throw new ArgumentException(_resourceMgr.GetString("NOAttributeMatch"));
|
|
}
|
|
else
|
|
{
|
|
XmlNode tmpNode = node.ChildNodes[0]; //.Item[0];
|
|
if (!tmpNode.NodeType.ToString().Equals("text", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new ArgumentException(_resourceMgr.GetString("NOAttributeMatch"));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(entry.Key.ToString()))
|
|
{
|
|
// XmlNode newnode = xmlfile.CreateNode(XmlNodeType.Attribute, ATTR_NIL_NAME, NS_XSI_URI);
|
|
XmlAttribute newnode = xmlfile.CreateAttribute(nameof(XmlNodeType.Attribute), ATTR_NIL_NAME, NS_XSI_URI);
|
|
newnode.Value = "true";
|
|
node.Attributes.Append(newnode);
|
|
// (newnode.Attributes.Item(0).FirstChild );
|
|
node.Value = string.Empty;
|
|
}
|
|
else
|
|
{
|
|
node.Attributes.RemoveNamedItem(ATTR_NIL_NAME);
|
|
node.InnerText = entry.Value.ToString();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
resultString = xmlfile.OuterXml;
|
|
break;
|
|
}
|
|
|
|
return resultString;
|
|
}
|
|
|
|
internal string GetXmlNs(string resUri)
|
|
{
|
|
return (@"xmlns:p=""" + StripParams(resUri) + @"""");
|
|
}
|
|
|
|
internal XmlNode GetXmlNode(string xmlString, string xpathpattern, string xmlnamespace)
|
|
{
|
|
XmlNode node = null;
|
|
XmlDocument xDoc = new XmlDocument();
|
|
xDoc.LoadXml(xmlString);
|
|
XmlNamespaceManager nsmgr = new XmlNamespaceManager(xDoc.NameTable);
|
|
if (!string.IsNullOrEmpty(xmlnamespace))
|
|
{
|
|
nsmgr.AddNamespace("cfg", xmlnamespace);
|
|
}
|
|
|
|
node = xDoc.SelectSingleNode(xpathpattern, nsmgr);
|
|
return node;
|
|
}
|
|
|
|
internal string CreateConnectionString(Uri ConnUri, int port, string computername, string applicationname)
|
|
{
|
|
string ConnectionString = null;
|
|
if (ConnUri != null)
|
|
{
|
|
ConnectionString = ConnUri.OriginalString;
|
|
}
|
|
else
|
|
{
|
|
if (computername == null && (port != 0 || applicationname != null))
|
|
{
|
|
// the user didn't give us a computer name but he gave a port and/or application name;
|
|
// in this case we need to have a computer name, to form the connection string;
|
|
// assume localhost
|
|
computername = "localhost";
|
|
}
|
|
|
|
ConnectionString = computername;
|
|
if (port != 0)
|
|
{
|
|
ConnectionString = ConnectionString + ":" + port;
|
|
}
|
|
|
|
if (applicationname != null)
|
|
{
|
|
ConnectionString = ConnectionString + "/" + applicationname;
|
|
}
|
|
}
|
|
|
|
return ConnectionString;
|
|
}
|
|
|
|
internal IWSManResourceLocator InitializeResourceLocator(Hashtable optionset, Hashtable selectorset, string fragment, Uri dialect, IWSManEx wsmanObj, Uri resourceuri)
|
|
{
|
|
string resource = null;
|
|
if (resourceuri != null)
|
|
{
|
|
resource = resourceuri.ToString();
|
|
}
|
|
|
|
if (selectorset != null)
|
|
{
|
|
resource += "?";
|
|
int i = 0;
|
|
foreach (DictionaryEntry entry in selectorset)
|
|
{
|
|
i++;
|
|
resource = resource + entry.Key.ToString() + "=" + entry.Value.ToString();
|
|
if (i < selectorset.Count)
|
|
resource += "+";
|
|
}
|
|
}
|
|
|
|
IWSManResourceLocator m_resource = null;
|
|
try
|
|
{
|
|
m_resource = (IWSManResourceLocator)wsmanObj.CreateResourceLocator(resource);
|
|
|
|
if (optionset != null)
|
|
{
|
|
foreach (DictionaryEntry entry in optionset)
|
|
{
|
|
if (entry.Value.ToString() == null)
|
|
{
|
|
m_resource.AddOption(entry.Key.ToString(), null, 1);
|
|
}
|
|
else
|
|
{
|
|
m_resource.AddOption(entry.Key.ToString(), entry.Value, 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(fragment))
|
|
{
|
|
m_resource.FragmentPath = fragment;
|
|
}
|
|
|
|
if (dialect != null)
|
|
{
|
|
m_resource.FragmentDialect = dialect.ToString();
|
|
}
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
AssertError(ex.Message, false, null);
|
|
}
|
|
|
|
return m_resource;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Used to resolve authentication from the parameters chosen by the user.
|
|
/// User has the following options:
|
|
/// 1. AuthMechanism + Credential
|
|
/// 2. CertificateThumbPrint
|
|
///
|
|
/// All the above are mutually exclusive.
|
|
/// </summary>
|
|
/// <exception cref="InvalidOperationException">
|
|
/// If there is ambiguity as specified above.
|
|
/// </exception>
|
|
internal static void ValidateSpecifiedAuthentication(AuthenticationMechanism authentication, PSCredential credential, string certificateThumbprint)
|
|
{
|
|
if ((credential != null) && (certificateThumbprint != null))
|
|
{
|
|
string message = FormatResourceMsgFromResourcetextS(
|
|
"AmbiguosAuthentication",
|
|
"CertificateThumbPrint", "credential");
|
|
|
|
throw new InvalidOperationException(message);
|
|
}
|
|
|
|
if ((authentication != AuthenticationMechanism.Default) &&
|
|
(authentication != AuthenticationMechanism.ClientCertificate) &&
|
|
(certificateThumbprint != null))
|
|
{
|
|
string message = FormatResourceMsgFromResourcetextS(
|
|
"AmbiguosAuthentication",
|
|
"CertificateThumbPrint", authentication.ToString());
|
|
|
|
throw new InvalidOperationException(message);
|
|
}
|
|
}
|
|
|
|
internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationMechanism authentication, SessionOption sessionoption, PSCredential credential, string connectionString, string certificateThumbprint, bool usessl)
|
|
{
|
|
ValidateSpecifiedAuthentication(authentication, credential, certificateThumbprint);
|
|
|
|
////if authentication is given
|
|
int sessionFlags = 0;
|
|
|
|
if (authentication.ToString() != null)
|
|
{
|
|
if (authentication.Equals(AuthenticationMechanism.None))
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUseNoAuthentication;
|
|
}
|
|
|
|
if (authentication.Equals(AuthenticationMechanism.Basic))
|
|
{
|
|
sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseBasic | (int)WSManSessionFlags.WSManFlagCredUserNamePassword;
|
|
}
|
|
|
|
if (authentication.Equals(AuthenticationMechanism.Negotiate))
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUseNegotiate;
|
|
}
|
|
|
|
if (authentication.Equals(AuthenticationMechanism.Kerberos))
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUseKerberos;
|
|
}
|
|
|
|
if (authentication.Equals(AuthenticationMechanism.Digest))
|
|
{
|
|
sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseDigest | (int)WSManSessionFlags.WSManFlagCredUserNamePassword;
|
|
}
|
|
|
|
if (authentication.Equals(AuthenticationMechanism.Credssp))
|
|
{
|
|
sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseCredSsp | (int)WSManSessionFlags.WSManFlagCredUserNamePassword;
|
|
}
|
|
|
|
if (authentication.Equals(AuthenticationMechanism.ClientCertificate))
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUseClientCertificate;
|
|
}
|
|
}
|
|
|
|
IWSManConnectionOptionsEx2 connObject = (IWSManConnectionOptionsEx2)wsmanObject.CreateConnectionOptions();
|
|
if (credential != null)
|
|
{
|
|
// connObject = (IWSManConnectionOptionsEx2)wsmanObject.CreateConnectionOptions();
|
|
System.Net.NetworkCredential nwCredential = new System.Net.NetworkCredential();
|
|
if (credential.UserName != null)
|
|
{
|
|
nwCredential = credential.GetNetworkCredential();
|
|
if (string.IsNullOrEmpty(nwCredential.Domain))
|
|
{
|
|
if (authentication.Equals(AuthenticationMechanism.Digest) || authentication.Equals(AuthenticationMechanism.Basic))
|
|
{
|
|
connObject.UserName = nwCredential.UserName;
|
|
}
|
|
else
|
|
{
|
|
// just wanted to not use null domain, empty is actually fine
|
|
connObject.UserName = "\\" + nwCredential.UserName;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
connObject.UserName = nwCredential.Domain + "\\" + nwCredential.UserName;
|
|
}
|
|
|
|
connObject.Password = nwCredential.Password;
|
|
if (!authentication.Equals(AuthenticationMechanism.Credssp) || !authentication.Equals(AuthenticationMechanism.Digest) || authentication.Equals(AuthenticationMechanism.Basic))
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagCredUserNamePassword;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (certificateThumbprint != null)
|
|
{
|
|
connObject.CertificateThumbprint = certificateThumbprint;
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUseClientCertificate;
|
|
}
|
|
|
|
if (sessionoption != null)
|
|
{
|
|
if (sessionoption.ProxyAuthentication != 0)
|
|
{
|
|
int ProxyAccessflags = 0;
|
|
int ProxyAuthenticationFlags = 0;
|
|
if (sessionoption.ProxyAccessType.Equals(ProxyAccessType.ProxyIEConfig))
|
|
{
|
|
ProxyAccessflags = connObject.ProxyIEConfig();
|
|
}
|
|
else if (sessionoption.ProxyAccessType.Equals(ProxyAccessType.ProxyAutoDetect))
|
|
{
|
|
ProxyAccessflags = connObject.ProxyAutoDetect();
|
|
}
|
|
else if (sessionoption.ProxyAccessType.Equals(ProxyAccessType.ProxyNoProxyServer))
|
|
{
|
|
ProxyAccessflags = connObject.ProxyNoProxyServer();
|
|
}
|
|
else if (sessionoption.ProxyAccessType.Equals(ProxyAccessType.ProxyWinHttpConfig))
|
|
{
|
|
ProxyAccessflags = connObject.ProxyWinHttpConfig();
|
|
}
|
|
|
|
if (sessionoption.ProxyAuthentication.Equals(ProxyAuthentication.Basic))
|
|
{
|
|
ProxyAuthenticationFlags = connObject.ProxyAuthenticationUseBasic();
|
|
}
|
|
else if (sessionoption.ProxyAuthentication.Equals(ProxyAuthentication.Negotiate))
|
|
{
|
|
ProxyAuthenticationFlags = connObject.ProxyAuthenticationUseNegotiate();
|
|
}
|
|
else if (sessionoption.ProxyAuthentication.Equals(ProxyAuthentication.Digest))
|
|
{
|
|
ProxyAuthenticationFlags = connObject.ProxyAuthenticationUseDigest();
|
|
}
|
|
|
|
if (sessionoption.ProxyCredential != null)
|
|
{
|
|
try
|
|
{
|
|
connObject.SetProxy(ProxyAccessflags, ProxyAuthenticationFlags, sessionoption.ProxyCredential.UserName, sessionoption.ProxyCredential.Password);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AssertError(ex.Message, false, null);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
connObject.SetProxy((int)sessionoption.ProxyAccessType, (int)sessionoption.ProxyAuthentication, null, null);
|
|
}
|
|
}
|
|
|
|
if (sessionoption.SkipCACheck)
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagSkipCACheck;
|
|
}
|
|
|
|
if (sessionoption.SkipCNCheck)
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagSkipCNCheck;
|
|
}
|
|
|
|
if (sessionoption.SPNPort > 0)
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagEnableSpnServerPort;
|
|
}
|
|
|
|
if (sessionoption.UseUtf16)
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUtf16;
|
|
}
|
|
else
|
|
{
|
|
// If UseUtf16 is false, then default Encoding is Utf8
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUtf8;
|
|
}
|
|
|
|
if (!sessionoption.UseEncryption)
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagNoEncryption;
|
|
}
|
|
|
|
if (sessionoption.SkipRevocationCheck)
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagSkipRevocationCheck;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If SessionOption is null then, default Encoding is Utf8
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUtf8;
|
|
}
|
|
|
|
if (usessl)
|
|
{
|
|
sessionFlags |= (int)WSManSessionFlags.WSManFlagUseSsl;
|
|
}
|
|
|
|
IWSManSession m_SessionObj = null;
|
|
try
|
|
{
|
|
m_SessionObj = (IWSManSession)wsmanObject.CreateSession(connectionString, sessionFlags, connObject);
|
|
if (sessionoption != null)
|
|
{
|
|
if (sessionoption.OperationTimeout > 0)
|
|
{
|
|
m_SessionObj.Timeout = sessionoption.OperationTimeout;
|
|
}
|
|
}
|
|
}
|
|
catch (COMException ex)
|
|
{
|
|
AssertError(ex.Message, false, null);
|
|
}
|
|
|
|
return m_SessionObj;
|
|
}
|
|
|
|
internal void CleanUp()
|
|
{
|
|
if (_sr != null)
|
|
{
|
|
_sr.Dispose();
|
|
_sr = null;
|
|
}
|
|
|
|
if (_fs != null)
|
|
{
|
|
_fs.Dispose();
|
|
_fs = null;
|
|
}
|
|
}
|
|
|
|
internal string GetFilterString(Hashtable seletorset)
|
|
{
|
|
StringBuilder filter = new StringBuilder();
|
|
foreach (DictionaryEntry entry in seletorset)
|
|
{
|
|
if (entry.Key != null && entry.Value != null)
|
|
{
|
|
filter.Append(entry.Key.ToString());
|
|
filter.Append('=');
|
|
filter.Append(entry.Value.ToString());
|
|
filter.Append('+');
|
|
}
|
|
}
|
|
|
|
filter.Remove(filter.ToString().Length - 1, 1);
|
|
return filter.ToString();
|
|
}
|
|
|
|
internal void AssertError(string ErrorMessage, bool IsWSManError, object targetobject)
|
|
{
|
|
if (IsWSManError)
|
|
{
|
|
XmlDocument ErrorDoc = new XmlDocument();
|
|
ErrorDoc.LoadXml(ErrorMessage);
|
|
InvalidOperationException ex = new InvalidOperationException(ErrorDoc.OuterXml);
|
|
ErrorRecord er = new ErrorRecord(ex, "WsManError", ErrorCategory.InvalidOperation, targetobject);
|
|
if (cmdletname != null)
|
|
{
|
|
cmdletname.ThrowTerminatingError(er);
|
|
}
|
|
else
|
|
{
|
|
_provider.ThrowTerminatingError(er);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
InvalidOperationException ex = new InvalidOperationException(ErrorMessage);
|
|
ErrorRecord er = new ErrorRecord(ex, "WsManError", ErrorCategory.InvalidOperation, targetobject);
|
|
if (cmdletname != null)
|
|
{
|
|
cmdletname.ThrowTerminatingError(er);
|
|
}
|
|
else
|
|
{
|
|
_provider.ThrowTerminatingError(er);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal string GetURIWithFilter(string uri, string filter, Hashtable selectorset, string operation)
|
|
{
|
|
StringBuilder sburi = new StringBuilder();
|
|
sburi.Append(uri);
|
|
sburi.Append('?');
|
|
|
|
if (operation.Equals("remove", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
sburi.Append(GetFilterString(selectorset));
|
|
if (sburi.ToString().EndsWith('?'))
|
|
{
|
|
sburi.Remove(sburi.Length - 1, 1);
|
|
}
|
|
}
|
|
|
|
return sburi.ToString();
|
|
}
|
|
|
|
/// <summary>
|
|
/// This method is used by Connect-WsMan Cmdlet and New-Item of WsMan Provider to create connection to WsMan.
|
|
/// </summary>
|
|
/// <param name="ParameterSetName"></param>
|
|
/// <param name="connectionuri"></param>
|
|
/// <param name="port"></param>
|
|
/// <param name="computername"></param>
|
|
/// <param name="applicationname"></param>
|
|
/// <param name="usessl"></param>
|
|
/// <param name="authentication"></param>
|
|
/// <param name="sessionoption"></param>
|
|
/// <param name="credential"></param>
|
|
/// <param name="certificateThumbprint"></param>
|
|
internal void CreateWsManConnection(string ParameterSetName, Uri connectionuri, int port, string computername, string applicationname, bool usessl, AuthenticationMechanism authentication, SessionOption sessionoption, PSCredential credential, string certificateThumbprint)
|
|
{
|
|
IWSManEx m_wsmanObject = (IWSManEx)new WSManClass();
|
|
try
|
|
{
|
|
string connectionStr = CreateConnectionString(connectionuri, port, computername, applicationname);
|
|
if (connectionuri != null)
|
|
{
|
|
// in the format http(s)://server[:port/applicationname]
|
|
string[] constrsplit = connectionStr.Split(":" + port + "/" + applicationname, StringSplitOptions.None);
|
|
string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None);
|
|
computername = constrsplit1[1].Trim();
|
|
}
|
|
|
|
IWSManSession m_session = CreateSessionObject(m_wsmanObject, authentication, sessionoption, credential, connectionStr, certificateThumbprint, usessl);
|
|
m_session.Identify(0);
|
|
string key = computername ?? "localhost";
|
|
|
|
AddtoDictionary(key, m_session);
|
|
}
|
|
catch (IndexOutOfRangeException)
|
|
{
|
|
AssertError(_resourceMgr.GetString("NotProperURI"), false, connectionuri);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
AssertError(ex.Message, false, computername);
|
|
}
|
|
finally
|
|
{
|
|
if (!string.IsNullOrEmpty(m_wsmanObject.Error))
|
|
{
|
|
AssertError(m_wsmanObject.Error, true, computername);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Verifies all the registry keys are set as expected. In case of failure .. try ecery second for 60 seconds before returning false.
|
|
/// </summary>
|
|
/// <param name="AllowFreshCredentialsValueShouldBePresent">True if trying to Enable CredSSP.</param>
|
|
/// <param name="DelegateComputer">Names of the delegate computer.</param>
|
|
/// <param name="applicationname">Name of the application.</param>
|
|
/// <returns>True if valid.</returns>
|
|
internal bool ValidateCreadSSPRegistryRetry(bool AllowFreshCredentialsValueShouldBePresent, string[] DelegateComputer, string applicationname)
|
|
{
|
|
for (int i = 0; i < 60; i++)
|
|
{
|
|
if (!ValidateCredSSPRegistry(AllowFreshCredentialsValueShouldBePresent, DelegateComputer, applicationname))
|
|
{
|
|
Thread.Sleep(1000);
|
|
}
|
|
else
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
internal bool ValidateCredSSPRegistry(bool AllowFreshCredentialsValueShouldBePresent, string[] DelegateComputer, string applicationname)
|
|
{
|
|
System.IntPtr NakedGPOCriticalSection = GpoNativeApi.EnterCriticalPolicySection(true);
|
|
|
|
try
|
|
{
|
|
RegistryKey rGPOLocalMachineKey = Registry.LocalMachine.OpenSubKey(
|
|
Registry_Path_Credentials_Delegation + @"\CredentialsDelegation",
|
|
RegistryKeyPermissionCheck.ReadWriteSubTree,
|
|
System.Security.AccessControl.RegistryRights.FullControl);
|
|
|
|
if (rGPOLocalMachineKey != null)
|
|
{
|
|
rGPOLocalMachineKey = rGPOLocalMachineKey.OpenSubKey(
|
|
Key_Allow_Fresh_Credentials,
|
|
RegistryKeyPermissionCheck.ReadWriteSubTree,
|
|
System.Security.AccessControl.RegistryRights.FullControl);
|
|
if (rGPOLocalMachineKey == null)
|
|
{
|
|
return !AllowFreshCredentialsValueShouldBePresent;
|
|
}
|
|
|
|
string[] valuenames = rGPOLocalMachineKey.GetValueNames();
|
|
if (valuenames.Length == 0)
|
|
{
|
|
return !AllowFreshCredentialsValueShouldBePresent;
|
|
}
|
|
|
|
List<string> RegValues = new List<string>();
|
|
foreach (string value in valuenames)
|
|
{
|
|
object keyvalue = rGPOLocalMachineKey.GetValue(value);
|
|
|
|
if (keyvalue != null && keyvalue.ToString().StartsWith(applicationname, StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (!AllowFreshCredentialsValueShouldBePresent)
|
|
{
|
|
// If calling Disable-CredSSP .. no value should start with "applicationName" regardless of the computer.
|
|
return false;
|
|
}
|
|
|
|
RegValues.Add(keyvalue.ToString());
|
|
}
|
|
}
|
|
|
|
if (AllowFreshCredentialsValueShouldBePresent)
|
|
{
|
|
// For all the keys that starts with "applicationName" make sure the delegated computer is listed.
|
|
foreach (string comp in DelegateComputer)
|
|
{
|
|
if (!RegValues.Contains(applicationname + "/" + comp))
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
bool GPOReleaseCriticalSection = GpoNativeApi.LeaveCriticalPolicySection(NakedGPOCriticalSection);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
internal static class WSManResourceLoader
|
|
{
|
|
internal static void LoadResourceData()
|
|
{
|
|
try
|
|
{
|
|
string filepath = System.Environment.ExpandEnvironmentVariables("%Windir%") + "\\System32\\Winrm\\" +
|
|
#if CORECLR
|
|
"0409" /* TODO: don't assume it is always English on CSS? */
|
|
#else
|
|
string.Concat("0", string.Format(CultureInfo.CurrentCulture, "{0:x2}", checked((uint)CultureInfo.CurrentUICulture.LCID)))
|
|
#endif
|
|
+ "\\" + "winrm.ini";
|
|
if (File.Exists(filepath))
|
|
{
|
|
FileStream _fs = new FileStream(filepath, FileMode.Open, FileAccess.Read);
|
|
StreamReader _sr = new StreamReader(_fs);
|
|
while (!_sr.EndOfStream)
|
|
{
|
|
string Line = _sr.ReadLine();
|
|
if (Line.Contains('='))
|
|
{
|
|
string[] arr = Line.Split('=', count: 2);
|
|
if (!ResourceValueCache.ContainsKey(arr[0].Trim()))
|
|
{
|
|
string value = arr[1].Trim('"');
|
|
ResourceValueCache.Add(arr[0].Trim(), value.Trim());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (IOException)
|
|
{
|
|
throw;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the resource value from WinRm.ini
|
|
/// from %windir%\system32\winrm\[Hexadecimal Language Folder]\winrm.ini.
|
|
/// </summary>
|
|
/// <param name="Key"></param>
|
|
/// <returns></returns>
|
|
internal static string GetResourceString(string Key)
|
|
{
|
|
// Checks whether resource values already loaded and loads.
|
|
if (ResourceValueCache.Count == 0)
|
|
{
|
|
LoadResourceData();
|
|
}
|
|
|
|
string value = string.Empty;
|
|
if (ResourceValueCache.ContainsKey(Key.Trim()))
|
|
{
|
|
ResourceValueCache.TryGetValue(Key.Trim(), out value);
|
|
}
|
|
|
|
return value.Trim();
|
|
}
|
|
|
|
/// <summary>
|
|
/// </summary>
|
|
private static readonly Dictionary<string, string> ResourceValueCache = new Dictionary<string, string>();
|
|
}
|
|
}
|