MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System.Collections;5using System.Collections.Concurrent;6using System.Collections.Generic;7using System.Collections.ObjectModel;8using System.Globalization;9using System.IO;10using System.Linq;11using System.Management.Automation.Language;12using System.Management.Automation.Runspaces;13using System.Net;14using System.Runtime.CompilerServices;15using System.Text.RegularExpressions;16using System.Threading;17using System.Xml;18 19using Microsoft.Management.Infrastructure;20using Microsoft.Management.Infrastructure.Options;21using Microsoft.PowerShell;22using Microsoft.PowerShell.Commands;23 24using Dbg = System.Management.Automation.Diagnostics;25 26namespace System.Management.Automation27{28 internal static class RemoteDiscoveryHelper29 {30 #region PSRP31 32 private static Collection<string> RehydrateHashtableKeys(PSObject pso, string propertyName)33 {34 const DeserializingTypeConverter.RehydrationFlags rehydrationFlags = DeserializingTypeConverter.RehydrationFlags.NullValueOk |35 DeserializingTypeConverter.RehydrationFlags.MissingPropertyOk;36 Hashtable hashtable = DeserializingTypeConverter.GetPropertyValue<Hashtable>(pso, propertyName, rehydrationFlags);37 if (hashtable == null)38 {39 return new Collection<string>();40 }41 else42 {43 List<string> list = hashtable44 .Keys45 .Cast<object>()46 .Where(static k => k != null)47 .Select(static k => k.ToString())48 .Where(static s => s != null)49 .ToList();50 return new Collection<string>(list);51 }52 }53 54 internal static PSModuleInfo RehydratePSModuleInfo(PSObject deserializedModuleInfo)55 {56 const DeserializingTypeConverter.RehydrationFlags rehydrationFlags = DeserializingTypeConverter.RehydrationFlags.NullValueOk |57 DeserializingTypeConverter.RehydrationFlags.MissingPropertyOk;58 string name = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "Name", rehydrationFlags);59 string path = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "Path", rehydrationFlags);60 PSModuleInfo moduleInfo = new PSModuleInfo(name, path, context: null, sessionState: null);61 62 moduleInfo.SetGuid(DeserializingTypeConverter.GetPropertyValue<Guid>(deserializedModuleInfo, "Guid", rehydrationFlags));63 moduleInfo.SetModuleType(DeserializingTypeConverter.GetPropertyValue<ModuleType>(deserializedModuleInfo, "ModuleType", rehydrationFlags));64 moduleInfo.SetVersion(DeserializingTypeConverter.GetPropertyValue<Version>(deserializedModuleInfo, "Version", rehydrationFlags));65 moduleInfo.SetHelpInfoUri(DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "HelpInfoUri", rehydrationFlags));66 67 moduleInfo.AccessMode = DeserializingTypeConverter.GetPropertyValue<ModuleAccessMode>(deserializedModuleInfo, "AccessMode", rehydrationFlags);68 moduleInfo.Author = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "Author", rehydrationFlags);69 moduleInfo.ClrVersion = DeserializingTypeConverter.GetPropertyValue<Version>(deserializedModuleInfo, "ClrVersion", rehydrationFlags);70 moduleInfo.CompanyName = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "CompanyName", rehydrationFlags);71 moduleInfo.Copyright = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "Copyright", rehydrationFlags);72 moduleInfo.Description = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "Description", rehydrationFlags);73 moduleInfo.DotNetFrameworkVersion = DeserializingTypeConverter.GetPropertyValue<Version>(deserializedModuleInfo, "DotNetFrameworkVersion", rehydrationFlags);74 moduleInfo.PowerShellHostName = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "PowerShellHostName", rehydrationFlags);75 moduleInfo.PowerShellHostVersion = DeserializingTypeConverter.GetPropertyValue<Version>(deserializedModuleInfo, "PowerShellHostVersion", rehydrationFlags);76 moduleInfo.PowerShellVersion = DeserializingTypeConverter.GetPropertyValue<Version>(deserializedModuleInfo, "PowerShellVersion", rehydrationFlags);77 moduleInfo.ProcessorArchitecture = DeserializingTypeConverter.GetPropertyValue<Reflection.ProcessorArchitecture>(deserializedModuleInfo, "ProcessorArchitecture", rehydrationFlags);78 79 moduleInfo.DeclaredAliasExports = RehydrateHashtableKeys(deserializedModuleInfo, "ExportedAliases");80 moduleInfo.DeclaredCmdletExports = RehydrateHashtableKeys(deserializedModuleInfo, "ExportedCmdlets");81 moduleInfo.DeclaredFunctionExports = RehydrateHashtableKeys(deserializedModuleInfo, "ExportedFunctions");82 moduleInfo.DeclaredVariableExports = RehydrateHashtableKeys(deserializedModuleInfo, "ExportedVariables");83 84 var compatiblePSEditions = DeserializingTypeConverter.GetPropertyValue<string[]>(deserializedModuleInfo, "CompatiblePSEditions", rehydrationFlags);85 if (compatiblePSEditions != null && compatiblePSEditions.Length > 0)86 {87 foreach (var edition in compatiblePSEditions)88 {89 moduleInfo.AddToCompatiblePSEditions(edition);90 }91 }92 93 // PowerShellGet related properties94 var tags = DeserializingTypeConverter.GetPropertyValue<string[]>(deserializedModuleInfo, "Tags", rehydrationFlags);95 if (tags != null && tags.Length > 0)96 {97 foreach (var tag in tags)98 {99 moduleInfo.AddToTags(tag);100 }101 }102 103 moduleInfo.ReleaseNotes = DeserializingTypeConverter.GetPropertyValue<string>(deserializedModuleInfo, "ReleaseNotes", rehydrationFlags);104 moduleInfo.ProjectUri = DeserializingTypeConverter.GetPropertyValue<Uri>(deserializedModuleInfo, "ProjectUri", rehydrationFlags);105 moduleInfo.LicenseUri = DeserializingTypeConverter.GetPropertyValue<Uri>(deserializedModuleInfo, "LicenseUri", rehydrationFlags);106 moduleInfo.IconUri = DeserializingTypeConverter.GetPropertyValue<Uri>(deserializedModuleInfo, "IconUri", rehydrationFlags);107 moduleInfo.RepositorySourceLocation = DeserializingTypeConverter.GetPropertyValue<Uri>(deserializedModuleInfo, "RepositorySourceLocation", rehydrationFlags);108 109 return moduleInfo;110 }111 112 private static EventHandler<DataAddedEventArgs> GetStreamForwarder<T>(Action<T> forwardingAction, bool swallowInvalidOperationExceptions = false)113 {114 // TODO/FIXME: ETW event for extended semantics streams115 return (object sender, DataAddedEventArgs eventArgs) =>116 {117 var psDataCollection = (PSDataCollection<T>)sender;118 foreach (T t in psDataCollection.ReadAll())119 {120 try121 {122 forwardingAction(t);123 }124 catch (InvalidOperationException)125 {126 if (!swallowInvalidOperationExceptions)127 {128 throw;129 }130 }131 }132 };133 }134 135 // This is a static field (instead of a constant) to make it possible to set through tests (and/or by customers if needed for a workaround)136 private static readonly int s_blockingCollectionCapacity = 1000;137 138 private static IEnumerable<PSObject> InvokeTopLevelPowerShell(139 PowerShell powerShell,140 PSCmdlet cmdlet,141 PSInvocationSettings invocationSettings,142 string errorMessageTemplate,143 CancellationToken cancellationToken)144 {145 using (var mergedOutput = new BlockingCollection<Func<PSCmdlet, IEnumerable<PSObject>>>(s_blockingCollectionCapacity))146 {147 var asyncOutput = new PSDataCollection<PSObject>();148 EventHandler<DataAddedEventArgs> outputHandler = GetStreamForwarder<PSObject>(149 output => mergedOutput.Add(_ => new[] { output }),150 swallowInvalidOperationExceptions: true);151 152 EventHandler<DataAddedEventArgs> errorHandler = GetStreamForwarder<ErrorRecord>(153 errorRecord => mergedOutput.Add(154 (PSCmdlet c) =>155 {156 errorRecord = GetErrorRecordForRemotePipelineInvocation(errorRecord, errorMessageTemplate);157 HandleErrorFromPipeline(c, errorRecord, powerShell);158 return Enumerable.Empty<PSObject>();159 }),160 swallowInvalidOperationExceptions: true);161 162 EventHandler<DataAddedEventArgs> warningHandler = GetStreamForwarder<WarningRecord>(163 warningRecord => mergedOutput.Add(164 (PSCmdlet c) =>165 {166 c.WriteWarning(warningRecord.Message);167 return Enumerable.Empty<PSObject>();168 }),169 swallowInvalidOperationExceptions: true);170 171 EventHandler<DataAddedEventArgs> verboseHandler = GetStreamForwarder<VerboseRecord>(172 verboseRecord => mergedOutput.Add(173 (PSCmdlet c) =>174 {175 c.WriteVerbose(verboseRecord.Message);176 return Enumerable.Empty<PSObject>();177 }),178 swallowInvalidOperationExceptions: true);179 180 EventHandler<DataAddedEventArgs> debugHandler = GetStreamForwarder<DebugRecord>(181 debugRecord => mergedOutput.Add(182 (PSCmdlet c) =>183 {184 c.WriteDebug(debugRecord.Message);185 return Enumerable.Empty<PSObject>();186 }),187 swallowInvalidOperationExceptions: true);188 189 EventHandler<DataAddedEventArgs> informationHandler = GetStreamForwarder<InformationRecord>(190 informationRecord => mergedOutput.Add(191 (PSCmdlet c) =>192 {193 c.WriteInformation(informationRecord);194 return Enumerable.Empty<PSObject>();195 }),196 swallowInvalidOperationExceptions: true);197 198 asyncOutput.DataAdded += outputHandler;199 powerShell.Streams.Error.DataAdded += errorHandler;200 powerShell.Streams.Warning.DataAdded += warningHandler;201 powerShell.Streams.Verbose.DataAdded += verboseHandler;202 powerShell.Streams.Debug.DataAdded += debugHandler;203 powerShell.Streams.Information.DataAdded += informationHandler;204 205 try206 {207 // TODO/FIXME: ETW event for PowerShell invocation208 209 var asyncResult = powerShell.BeginInvoke<PSObject, PSObject>(210 input: null,211 output: asyncOutput,212 settings: invocationSettings,213 callback: delegate214 {215 try216 {217 mergedOutput.CompleteAdding();218 }219 catch (InvalidOperationException)220 // ignore exceptions thrown because mergedOutput.CompleteAdding was called221 {222 }223 },224 state: null);225 226 using (cancellationToken.Register(powerShell.Stop))227 {228 try229 {230 foreach (Func<PSCmdlet, IEnumerable<PSObject>> mergedOutputItem in mergedOutput.GetConsumingEnumerable())231 {232 foreach (PSObject outputObject in mergedOutputItem(cmdlet))233 {234 yield return outputObject;235 }236 }237 }238 finally239 {240 mergedOutput.CompleteAdding();241 powerShell.EndInvoke(asyncResult);242 }243 }244 }245 finally246 {247 asyncOutput.DataAdded -= outputHandler;248 powerShell.Streams.Error.DataAdded -= errorHandler;249 powerShell.Streams.Warning.DataAdded -= warningHandler;250 powerShell.Streams.Verbose.DataAdded -= verboseHandler;251 powerShell.Streams.Debug.DataAdded -= debugHandler;252 powerShell.Streams.Information.DataAdded -= informationHandler;253 }254 }255 }256 257 private static IEnumerable<PSObject> InvokeNestedPowerShell(258 PowerShell powerShell,259 PSCmdlet cmdlet,260 PSInvocationSettings invocationSettings,261 string errorMessageTemplate,262 CancellationToken cancellationToken)263 {264 EventHandler<DataAddedEventArgs> errorHandler = GetStreamForwarder<ErrorRecord>(265 (ErrorRecord errorRecord) =>266 {267 errorRecord = GetErrorRecordForRemotePipelineInvocation(errorRecord, errorMessageTemplate);268 HandleErrorFromPipeline(cmdlet, errorRecord, powerShell);269 });270 powerShell.Streams.Error.DataAdded += errorHandler;271 272 try273 {274 using (cancellationToken.Register(powerShell.Stop))275 {276 // TODO/FIXME: ETW event for PowerShell invocation277 278 foreach (PSObject outputObject in powerShell.Invoke<PSObject>(null, invocationSettings))279 {280 yield return outputObject;281 }282 }283 }284 finally285 {286 powerShell.Streams.Error.DataAdded -= errorHandler;287 }288 }289 290 private static void CopyParameterFromCmdletToPowerShell(Cmdlet cmdlet, PowerShell powerShell, string parameterName)291 {292 object parameterValue;293 if (!cmdlet.MyInvocation.BoundParameters.TryGetValue(parameterName, out parameterValue))294 {295 return;296 }297 298 var commandParameter = new CommandParameter(parameterName, parameterValue);299 foreach (var command in powerShell.Commands.Commands)300 {301 if (command.Parameters.Any(existingParameter => existingParameter.Name.Equals(parameterName, StringComparison.OrdinalIgnoreCase)))302 {303 continue;304 }305 306 command.Parameters.Add(commandParameter);307 }308 }309 310 internal static ErrorRecord GetErrorRecordForProcessingOfCimModule(Exception innerException, string moduleName)311 {312 string errorMessage = string.Format(313 CultureInfo.InvariantCulture,314 Modules.RemoteDiscoveryFailedToProcessRemoteModule,315 moduleName,316 innerException.Message);317 318 Exception outerException = new InvalidOperationException(errorMessage, innerException);319 ErrorRecord errorRecord = new ErrorRecord(outerException, innerException.GetType().Name, ErrorCategory.NotSpecified, moduleName);320 return errorRecord;321 }322 323 private const string DiscoveryProviderNotFoundErrorId = "DiscoveryProviderNotFound";324 325 private static ErrorRecord GetErrorRecordForRemoteDiscoveryProvider(Exception innerException)326 {327 CimException cimException = innerException as CimException;328 if ((cimException != null) &&329 ((cimException.NativeErrorCode == NativeErrorCode.InvalidNamespace) ||330 (cimException.NativeErrorCode == NativeErrorCode.InvalidClass) ||331 (cimException.NativeErrorCode == NativeErrorCode.MethodNotFound) ||332 (cimException.NativeErrorCode == NativeErrorCode.MethodNotAvailable)))333 {334 string errorMessage = string.Format(335 CultureInfo.InvariantCulture,336 Modules.RemoteDiscoveryProviderNotFound,337 innerException.Message);338 Exception outerException = new InvalidOperationException(errorMessage, innerException);339 ErrorRecord errorRecord = new ErrorRecord(outerException, DiscoveryProviderNotFoundErrorId, ErrorCategory.NotImplemented, null);340 return errorRecord;341 }342 else343 {344 string errorMessage = string.Format(345 CultureInfo.InvariantCulture,346 Modules.RemoteDiscoveryFailureFromDiscoveryProvider,347 innerException.Message);348 Exception outerException = new InvalidOperationException(errorMessage, innerException);349 ErrorRecord errorRecord = new ErrorRecord(outerException, "DiscoveryProviderFailure", ErrorCategory.NotSpecified, null);350 return errorRecord;351 }352 }353 354 private static ErrorRecord GetErrorRecordForRemotePipelineInvocation(Exception innerException, string errorMessageTemplate)355 {356 string errorMessage = string.Format(357 CultureInfo.InvariantCulture,358 errorMessageTemplate,359 innerException.Message);360 Exception outerException = new InvalidOperationException(errorMessage, innerException);361 362 RemoteException remoteException = innerException as RemoteException;363 ErrorRecord remoteErrorRecord = remoteException?.ErrorRecord;364 string errorId = remoteErrorRecord != null ? remoteErrorRecord.FullyQualifiedErrorId : innerException.GetType().Name;365 ErrorCategory errorCategory = remoteErrorRecord != null ? remoteErrorRecord.CategoryInfo.Category : ErrorCategory.NotSpecified;366 ErrorRecord errorRecord = new ErrorRecord(outerException, errorId, errorCategory, null);367 368 return errorRecord;369 }370 371 private static ErrorRecord GetErrorRecordForRemotePipelineInvocation(ErrorRecord innerErrorRecord, string errorMessageTemplate)372 {373 string innerErrorMessage;374 if (innerErrorRecord.ErrorDetails != null && innerErrorRecord.ErrorDetails.Message != null)375 {376 innerErrorMessage = innerErrorRecord.ErrorDetails.Message;377 }378 else if (innerErrorRecord.Exception != null && innerErrorRecord.Exception.Message != null)379 {380 innerErrorMessage = innerErrorRecord.Exception.Message;381 }382 else383 {384 innerErrorMessage = innerErrorRecord.ToString();385 }386 387 string errorMessage = string.Format(388 CultureInfo.InvariantCulture,389 errorMessageTemplate,390 innerErrorMessage);391 392 ErrorRecord outerErrorRecord = new ErrorRecord(innerErrorRecord, null /* null means: do not replace the exception */);393 ErrorDetails outerErrorDetails = new ErrorDetails(errorMessage);394 outerErrorRecord.ErrorDetails = outerErrorDetails;395 396 return outerErrorRecord;397 }398 399 private static IEnumerable<T> EnumerateWithCatch<T>(IEnumerable<T> enumerable, Action<Exception> exceptionHandler)400 {401 IEnumerator<T> enumerator = null;402 try403 {404 enumerator = enumerable.GetEnumerator();405 }406 catch (Exception e)407 {408 exceptionHandler(e);409 }410 411 if (enumerator != null)412 using (enumerator)413 {414 bool gotResults = false;415 do416 {417 try418 {419 gotResults = false;420 gotResults = enumerator.MoveNext();421 }422 catch (Exception e)423 {424 exceptionHandler(e);425 }426 427 if (gotResults)428 {429 T currentItem = default(T);430 bool gotCurrentItem = false;431 try432 {433 currentItem = enumerator.Current;434 gotCurrentItem = true;435 }436 catch (Exception e)437 {438 exceptionHandler(e);439 }440 441 if (gotCurrentItem)442 {443 yield return currentItem;444 }445 else446 {447 yield break;448 }449 }450 } while (gotResults);451 }452 }453 454 private static void HandleErrorFromPipeline(Cmdlet cmdlet, ErrorRecord errorRecord, PowerShell powerShell)455 {456 if (!cmdlet.MyInvocation.ExpectingInput)457 {458 if (((powerShell.Runspace != null) && (powerShell.Runspace.RunspaceStateInfo.State != RunspaceState.Opened)) ||459 ((powerShell.RunspacePool != null) && (powerShell.RunspacePool.RunspacePoolStateInfo.State != RunspacePoolState.Opened)))460 {461 cmdlet.ThrowTerminatingError(errorRecord);462 }463 }464 465 cmdlet.WriteError(errorRecord);466 }467 468 internal static IEnumerable<PSObject> InvokePowerShell(469 PowerShell powerShell,470 PSCmdlet cmdlet,471 string errorMessageTemplate,472 CancellationToken cancellationToken)473 {474 CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "ErrorAction");475 CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "WarningAction");476 CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "InformationAction");477 CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "Verbose");478 CopyParameterFromCmdletToPowerShell(cmdlet, powerShell, "Debug");479 480 var invocationSettings = new PSInvocationSettings { Host = cmdlet.Host };481 482 // TODO/FIXME: ETW events for the output stream483 IEnumerable<PSObject> outputStream = powerShell.IsNested484 ? InvokeNestedPowerShell(powerShell, cmdlet, invocationSettings, errorMessageTemplate, cancellationToken)485 : InvokeTopLevelPowerShell(powerShell, cmdlet, invocationSettings, errorMessageTemplate, cancellationToken);486 487 return EnumerateWithCatch(488 outputStream,489 (Exception exception) =>490 {491 ErrorRecord errorRecord = GetErrorRecordForRemotePipelineInvocation(exception, errorMessageTemplate);492 HandleErrorFromPipeline(cmdlet, errorRecord, powerShell);493 });494 }495 496 #endregion PSRP497 498 #region CIM499 500 private const string DiscoveryProviderNamespace = "root/Microsoft/Windows/Powershellv3";501 private const string DiscoveryProviderModuleClass = "PS_Module";502 private const string DiscoveryProviderFileClass = "PS_ModuleFile";503 private const string DiscoveryProviderAssociationClass = "PS_ModuleToModuleFile";504 505 private static T GetPropertyValue<T>(CimInstance cimInstance, string propertyName, T defaultValue)506 {507 CimProperty cimProperty = cimInstance.CimInstanceProperties[propertyName];508 if (cimProperty == null)509 {510 return defaultValue;511 }512 513 object propertyValue = cimProperty.Value;514 if (propertyValue is T)515 {516 return (T)propertyValue;517 }518 519 if (propertyValue is string)520 {521 string stringValue = (string)propertyValue;522 try523 {524 if (typeof(T) == typeof(bool))525 {526 return (T)(object)XmlConvert.ToBoolean(stringValue);527 }528 else if (typeof(T) == typeof(UInt16))529 {530 return (T)(object)UInt16.Parse(stringValue, CultureInfo.InvariantCulture);531 }532 else if (typeof(T) == typeof(byte[]))533 {534 byte[] contentBytes = Convert.FromBase64String(stringValue);535 byte[] lengthBytes = BitConverter.GetBytes(contentBytes.Length + 4);536 if (BitConverter.IsLittleEndian)537 {538 Array.Reverse(lengthBytes);539 }540 541 return (T)(object)(lengthBytes.Concat(contentBytes).ToArray());542 }543 }544 catch (Exception)545 {546 return defaultValue;547 }548 }549 550 return defaultValue;551 }552 553 internal enum CimFileCode554 {555 Unknown = 0,556 PsdV1,557 TypesV1,558 FormatV1,559 CmdletizationV1,560 }561 562 internal abstract class CimModuleFile563 {564 public CimFileCode FileCode565 {566 get567 {568 if (this.FileName.EndsWith(".psd1", StringComparison.OrdinalIgnoreCase))569 {570 return CimFileCode.PsdV1;571 }572 573 if (this.FileName.EndsWith(".cdxml", StringComparison.OrdinalIgnoreCase))574 {575 return CimFileCode.CmdletizationV1;576 }577 578 if (this.FileName.EndsWith(".types.ps1xml", StringComparison.OrdinalIgnoreCase))579 {580 return CimFileCode.TypesV1;581 }582 583 if (this.FileName.EndsWith(".format.ps1xml", StringComparison.OrdinalIgnoreCase))584 {585 return CimFileCode.FormatV1;586 }587 588 return CimFileCode.Unknown;589 }590 }591 592 public abstract string FileName { get; }593 594 internal abstract byte[] RawFileDataCore { get; }595 596 public byte[] RawFileData597 {598 get { return this.RawFileDataCore.Skip(4).ToArray(); }599 }600 601 public string FileData602 {603 get604 {605 if (_fileData == null)606 {607 using (var ms = new MemoryStream(this.RawFileData))608 using (var sr = new StreamReader(ms, detectEncodingFromByteOrderMarks: true))609 {610 _fileData = sr.ReadToEnd();611 }612 }613 614 return _fileData;615 }616 }617 618 private string _fileData;619 }620 621 internal class CimModule622 {623 private readonly CimInstance _baseObject;624 625 internal CimModule(CimInstance baseObject)626 {627 Dbg.Assert(baseObject != null, "Caller should make sure baseObject != null");628 Dbg.Assert(629 baseObject.CimSystemProperties.ClassName.Equals(DiscoveryProviderModuleClass, StringComparison.OrdinalIgnoreCase),630 "Caller should make sure baseObject is an instance of the right CIM class");631 632 _baseObject = baseObject;633 }634 635 public string ModuleName636 {637 get638 {639 var rawModuleName = GetPropertyValue<string>(_baseObject, "ModuleName", string.Empty);640 return Path.GetFileName(rawModuleName);641 }642 }643 644 private enum DiscoveredModuleType : ushort645 {646 Unknown = 0,647 Cim = 1,648 }649 650 public bool IsPsCimModule651 {652 get653 {654 UInt16 moduleTypeInt = GetPropertyValue<UInt16>(_baseObject, "ModuleType", 0);655 DiscoveredModuleType moduleType = (DiscoveredModuleType)moduleTypeInt;656 bool isPsCimModule = (moduleType == DiscoveredModuleType.Cim);657 return isPsCimModule;658 }659 }660 661 public CimModuleFile MainManifest662 {663 get664 {665 byte[] rawFileData = GetPropertyValue<byte[]>(_baseObject, "moduleManifestFileData", Array.Empty<byte>());666 return new CimModuleManifestFile(this.ModuleName + ".psd1", rawFileData);667 }668 }669 670 public IEnumerable<CimModuleFile> ModuleFiles671 {672 get { return _moduleFiles; }673 }674 675 internal void FetchAllModuleFiles(CimSession cimSession, string cimNamespace, CimOperationOptions operationOptions)676 {677 IEnumerable<CimInstance> associatedInstances = cimSession.EnumerateAssociatedInstances(678 cimNamespace,679 _baseObject,680 DiscoveryProviderAssociationClass,681 DiscoveryProviderFileClass,682 "Antecedent",683 "Dependent",684 operationOptions);685 686 IEnumerable<CimModuleFile> associatedFiles = associatedInstances.Select(static i => new CimModuleImplementationFile(i));687 _moduleFiles = associatedFiles.ToList();688 }689 690 private List<CimModuleFile> _moduleFiles;691 692 private sealed class CimModuleManifestFile : CimModuleFile693 {694 internal CimModuleManifestFile(string fileName, byte[] rawFileData)695 {696 Dbg.Assert(fileName != null, "Caller should make sure fileName != null");697 Dbg.Assert(rawFileData != null, "Caller should make sure rawFileData != null");698 699 FileName = fileName;700 RawFileDataCore = rawFileData;701 }702 703 public override string FileName { get; }704 705 internal override byte[] RawFileDataCore { get; }706 }707 708 private sealed class CimModuleImplementationFile : CimModuleFile709 {710 private readonly CimInstance _baseObject;711 712 internal CimModuleImplementationFile(CimInstance baseObject)713 {714 Dbg.Assert(baseObject != null, "Caller should make sure baseObject != null");715 Dbg.Assert(716 baseObject.CimSystemProperties.ClassName.Equals(DiscoveryProviderFileClass, StringComparison.OrdinalIgnoreCase),717 "Caller should make sure baseObject is an instance of the right CIM class");718 719 _baseObject = baseObject;720 }721 722 public override string FileName723 {724 get725 {726 string rawFileName = GetPropertyValue<string>(_baseObject, "FileName", string.Empty);727 return Path.GetFileName(rawFileName);728 }729 }730 731 internal override byte[] RawFileDataCore732 {733 get { return GetPropertyValue<byte[]>(_baseObject, "FileData", Array.Empty<byte>()); }734 }735 }736 }737 738 internal static IEnumerable<CimModule> GetCimModules(739 CimSession cimSession,740 Uri resourceUri,741 string cimNamespace,742 IEnumerable<string> moduleNamePatterns,743 bool onlyManifests,744 Cmdlet cmdlet,745 CancellationToken cancellationToken)746 {747 moduleNamePatterns ??= new[] { "*" };748 HashSet<string> alreadyEmittedNamesOfCimModules = new HashSet<string>(StringComparer.OrdinalIgnoreCase);749 750 IEnumerable<CimModule> remoteModules = moduleNamePatterns751 .SelectMany(moduleNamePattern =>752 RemoteDiscoveryHelper.GetCimModules(cimSession, resourceUri, cimNamespace, moduleNamePattern, onlyManifests, cmdlet, cancellationToken));753 foreach (CimModule remoteModule in remoteModules)754 {755 if (!alreadyEmittedNamesOfCimModules.Contains(remoteModule.ModuleName))756 {757 alreadyEmittedNamesOfCimModules.Add(remoteModule.ModuleName);758 yield return remoteModule;759 }760 }761 }762 763 private static IEnumerable<CimModule> GetCimModules(764 CimSession cimSession,765 Uri resourceUri,766 string cimNamespace,767 string moduleNamePattern,768 bool onlyManifests,769 Cmdlet cmdlet,770 CancellationToken cancellationToken)771 {772 Dbg.Assert(cimSession != null, "Caller should verify cimSession != null");773 Dbg.Assert(moduleNamePattern != null, "Caller should verify that moduleNamePattern != null");774 775 const WildcardOptions wildcardOptions = WildcardOptions.IgnoreCase | WildcardOptions.CultureInvariant;776 var wildcardPattern = WildcardPattern.Get(moduleNamePattern, wildcardOptions);777 string dosWildcard = WildcardPatternToDosWildcardParser.Parse(wildcardPattern);778 779 var options = new CimOperationOptions { CancellationToken = cancellationToken };780 options.SetCustomOption("PS_ModuleNamePattern", dosWildcard, mustComply: false);781 if (resourceUri != null)782 {783 options.ResourceUri = resourceUri;784 }785 786 if (string.IsNullOrEmpty(cimNamespace) && (resourceUri == null))787 {788 cimNamespace = DiscoveryProviderNamespace;789 }790 791 // TODO/FIXME: ETW for method invocation792 IEnumerable<CimInstance> syncResults = cimSession.EnumerateInstances(793 cimNamespace,794 DiscoveryProviderModuleClass,795 options);796 // TODO/FIXME: ETW for method results797 IEnumerable<CimModule> cimModules = syncResults798 .Select(static cimInstance => new CimModule(cimInstance))799 .Where(cimModule => wildcardPattern.IsMatch(cimModule.ModuleName));800 801 if (!onlyManifests)802 {803 cimModules = cimModules.Select(804 (CimModule cimModule) =>805 {806 cimModule.FetchAllModuleFiles(cimSession, cimNamespace, options);807 return cimModule;808 });809 }810 811 return EnumerateWithCatch(812 cimModules,813 (Exception exception) =>814 {815 ErrorRecord errorRecord = GetErrorRecordForRemoteDiscoveryProvider(exception);816 if (!cmdlet.MyInvocation.ExpectingInput)817 {818 if (errorRecord.FullyQualifiedErrorId.Contains(DiscoveryProviderNotFoundErrorId, StringComparison.OrdinalIgnoreCase)819 || cancellationToken.IsCancellationRequested820 || exception is OperationCanceledException821 || !cimSession.TestConnection())822 {823 cmdlet.ThrowTerminatingError(errorRecord);824 }825 }826 827 cmdlet.WriteError(errorRecord);828 });829 }830 831 internal static Hashtable RewriteManifest(Hashtable originalManifest)832 {833 return RewriteManifest(originalManifest, null, null, null);834 }835 836 private static readonly string[] s_manifestEntriesToKeepAsString = new[] {837 "GUID",838 "Author",839 "CompanyName",840 "Copyright",841 "ModuleVersion",842 "Description",843 "HelpInfoURI",844 };845 846 private static readonly string[] s_manifestEntriesToKeepAsStringArray = new[] {847 "FunctionsToExport",848 "VariablesToExport",849 "AliasesToExport",850 "CmdletsToExport",851 };852 853 internal static Hashtable RewriteManifest(854 Hashtable originalManifest,855 IEnumerable<string> nestedModules,856 IEnumerable<string> typesToProcess,857 IEnumerable<string> formatsToProcess)858 {859 nestedModules ??= Array.Empty<string>();860 typesToProcess ??= Array.Empty<string>();861 formatsToProcess ??= Array.Empty<string>();862 863 var newManifest = new Hashtable(StringComparer.OrdinalIgnoreCase);864 newManifest["NestedModules"] = nestedModules;865 newManifest["TypesToProcess"] = typesToProcess;866 newManifest["FormatsToProcess"] = formatsToProcess;867 newManifest["PrivateData"] = originalManifest["PrivateData"];868 869 foreach (DictionaryEntry entry in originalManifest)870 {871 if (s_manifestEntriesToKeepAsString.Contains(entry.Key as string, StringComparer.OrdinalIgnoreCase))872 {873 var value = (string)LanguagePrimitives.ConvertTo(entry.Value, typeof(string), CultureInfo.InvariantCulture);874 newManifest[entry.Key] = value;875 }876 else if (s_manifestEntriesToKeepAsStringArray.Contains(entry.Key as string, StringComparer.OrdinalIgnoreCase))877 {878 var values = (string[])LanguagePrimitives.ConvertTo(entry.Value, typeof(string[]), CultureInfo.InvariantCulture);879 newManifest[entry.Key] = values;880 }881 }882 883 return newManifest;884 }885 886 private static CimCredential GetCimCredentials(PasswordAuthenticationMechanism authenticationMechanism, PSCredential credential)887 {888 NetworkCredential networkCredential = credential.GetNetworkCredential();889 return new CimCredential(authenticationMechanism, networkCredential.Domain, networkCredential.UserName, credential.Password);890 }891 892 private static Exception GetExceptionWhenAuthenticationRequiresCredential(string authentication)893 {894 string errorMessage = string.Format(895 CultureInfo.InvariantCulture,896 RemotingErrorIdStrings.AuthenticationMechanismRequiresCredential,897 authentication);898 throw new ArgumentException(errorMessage);899 }900 901 private static CimCredential GetCimCredentials(string authentication, PSCredential credential)902 {903 if (authentication == null || (authentication.Equals("Default", StringComparison.OrdinalIgnoreCase)))904 {905 if (credential == null)906 {907 return null;908 }909 else910 {911 return GetCimCredentials(PasswordAuthenticationMechanism.Default, credential);912 }913 }914 915 if (authentication.Equals("Basic", StringComparison.OrdinalIgnoreCase))916 {917 if (credential == null)918 {919 throw GetExceptionWhenAuthenticationRequiresCredential(authentication);920 }921 else922 {923 return GetCimCredentials(PasswordAuthenticationMechanism.Basic, credential);924 }925 }926 927 if (authentication.Equals("Negotiate", StringComparison.OrdinalIgnoreCase))928 {929 if (credential == null)930 {931 return new CimCredential(ImpersonatedAuthenticationMechanism.Negotiate);932 }933 else934 {935 return GetCimCredentials(PasswordAuthenticationMechanism.Negotiate, credential);936 }937 }938 939 if (authentication.Equals("CredSSP", StringComparison.OrdinalIgnoreCase))940 {941 if (credential == null)942 {943 throw GetExceptionWhenAuthenticationRequiresCredential(authentication);944 }945 else946 {947 return GetCimCredentials(PasswordAuthenticationMechanism.CredSsp, credential);948 }949 }950 951 if (authentication.Equals("Digest", StringComparison.OrdinalIgnoreCase))952 {953 if (credential == null)954 {955 throw GetExceptionWhenAuthenticationRequiresCredential(authentication);956 }957 else958 {959 return GetCimCredentials(PasswordAuthenticationMechanism.Digest, credential);960 }961 }962 963 if (authentication.Equals("Kerberos", StringComparison.OrdinalIgnoreCase))964 {965 if (credential == null)966 {967 return new CimCredential(ImpersonatedAuthenticationMechanism.Kerberos);968 }969 else970 {971 return GetCimCredentials(PasswordAuthenticationMechanism.Kerberos, credential);972 }973 }974 975 Dbg.Assert(false, "Unrecognized authentication mechanism [ValidateSet should prevent that from happening]");976 throw new ArgumentOutOfRangeException(nameof(authentication));977 }978 979 internal static CimSession CreateCimSession(980 string computerName,981 PSCredential credential,982 string authentication,983 bool isLocalHost,984 PSCmdlet cmdlet,985 CancellationToken cancellationToken)986 {987 if (isLocalHost)988 {989 return CimSession.Create(null);990 }991 992 var sessionOptions = new CimSessionOptions();993 994 CimCredential cimCredentials = GetCimCredentials(authentication, credential);995 if (cimCredentials != null)996 {997 sessionOptions.AddDestinationCredentials(cimCredentials);998 }999 1000 CimSession cimSession = CimSession.Create(computerName, sessionOptions);1001 return cimSession;1002 }1003 1004 internal static Hashtable ConvertCimModuleFileToManifestHashtable(RemoteDiscoveryHelper.CimModuleFile cimModuleFile, string temporaryModuleManifestPath, ModuleCmdletBase cmdlet, ref bool containedErrors)1005 {1006 Dbg.Assert(cimModuleFile.FileCode == RemoteDiscoveryHelper.CimFileCode.PsdV1, "Caller should verify the file is of the right type");1007 1008 ScriptBlockAst scriptBlockAst = null;1009 if (!containedErrors)1010 {1011 System.Management.Automation.Language.Token[] throwAwayTokens;1012 ParseError[] parseErrors;1013 scriptBlockAst = System.Management.Automation.Language.Parser.ParseInput(cimModuleFile.FileData, temporaryModuleManifestPath, out throwAwayTokens, out parseErrors);1014 if ((scriptBlockAst == null) || (parseErrors != null && parseErrors.Length > 0))1015 {1016 containedErrors = true;1017 }1018 }1019 1020 Hashtable data = null;1021 if (!containedErrors)1022 {1023 ScriptBlock scriptBlock = new ScriptBlock(scriptBlockAst, isFilter: false);1024 data = cmdlet.LoadModuleManifestData(1025 temporaryModuleManifestPath,1026 scriptBlock,1027 ModuleCmdletBase.ModuleManifestMembers,1028 0 /* - don't write errors, don't load elements, don't return null on first error */,1029 ref containedErrors);1030 }1031 1032 return data;1033 }1034 1035 #endregion CIM1036 1037 #region Protocol/transport agnostic functionality1038 1039 internal static string GetModulePath(string remoteModuleName, Version remoteModuleVersion, string computerName, Runspace localRunspace)1040 {1041 computerName ??= string.Empty;1042 1043 string sanitizedRemoteModuleName = Regex.Replace(remoteModuleName, "[^a-zA-Z0-9]", string.Empty);1044 string sanitizedComputerName = Regex.Replace(computerName, "[^a-zA-Z0-9]", string.Empty);1045 string moduleName = string.Format(1046 CultureInfo.InvariantCulture,1047 "remoteIpMoProxy_{0}_{1}_{2}_{3}",1048 sanitizedRemoteModuleName.Substring(0, Math.Min(sanitizedRemoteModuleName.Length, 100)),1049 remoteModuleVersion,1050 sanitizedComputerName.Substring(0, Math.Min(sanitizedComputerName.Length, 100)),1051 localRunspace.InstanceId);1052 string modulePath = Path.Combine(Path.GetTempPath(), moduleName);1053 return modulePath;1054 }1055 1056 internal static void AssociatePSModuleInfoWithSession(PSModuleInfo moduleInfo, CimSession cimSession, Uri resourceUri, string cimNamespace)1057 {1058 AssociatePSModuleInfoWithSession(moduleInfo, (object)new Tuple<CimSession, Uri, string>(cimSession, resourceUri, cimNamespace));1059 }1060 1061 internal static void AssociatePSModuleInfoWithSession(PSModuleInfo moduleInfo, PSSession psSession)1062 {1063 AssociatePSModuleInfoWithSession(moduleInfo, (object)psSession);1064 }1065 1066 private static void AssociatePSModuleInfoWithSession(PSModuleInfo moduleInfo, object weaklyTypedSession)1067 {1068 s_moduleInfoToSession.Add(moduleInfo, weaklyTypedSession);1069 }1070 1071 private static readonly ConditionalWeakTable<PSModuleInfo, object> s_moduleInfoToSession = new ConditionalWeakTable<PSModuleInfo, object>();1072 1073 internal static void DispatchModuleInfoProcessing(1074 PSModuleInfo moduleInfo,1075 Action localAction,1076 Action<CimSession, Uri, string> cimSessionAction,1077 Action<PSSession> psSessionAction)1078 {1079 object weaklyTypeSession;1080 if (!s_moduleInfoToSession.TryGetValue(moduleInfo, out weaklyTypeSession))1081 {1082 localAction();1083 return;1084 }1085 1086 Tuple<CimSession, Uri, string> cimSessionInfo = weaklyTypeSession as Tuple<CimSession, Uri, string>;1087 if (cimSessionInfo != null)1088 {1089 cimSessionAction(cimSessionInfo.Item1, cimSessionInfo.Item2, cimSessionInfo.Item3);1090 return;1091 }1092 1093 PSSession psSession = weaklyTypeSession as PSSession;1094 if (psSession != null)1095 {1096 psSessionAction(psSession);1097 return;1098 }1099 1100 Dbg.Assert(false, "PSModuleInfo was associated with an unrecognized session type");1101 }1102 1103 #endregion1104 }1105}1106 