Integrate changes between [SD:717473] and [SD:725290]

This commit is contained in:
PowerShell Team authored and Sergei Vorobev committed 2016-07-28 18:02:24 -07:00
1 parent 2280bea11e
commit 0dd425da19
28 files changed
+848 -342

No files matched your search

@@ -110,17 +110,15 @@ namespace Microsoft.PowerShell.Commands
protected override void ProcessRecord()
{
// In ConstrainedLanguage, XAML workflows are not supported (even from a trusted FullLanguage state),
// since we can't prevent tampering.
// unless they are signed in-box OS binaries.
bool checkSignatures = false;
if ((SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ||
(this.SessionState.LanguageMode == PSLanguageMode.ConstrainedLanguage))
{
// However, this static internal property can be changed by tests that can already run a script
// in full-language mode.
PropertyInfo xamlProperty = typeof(SystemPolicy).GetProperty("XamlWorkflowSupported", BindingFlags.NonPublic | BindingFlags.Static);
if (! ((bool) xamlProperty.GetValue(null, null)))
{
throw new NotSupportedException(Resources.XamlWorkflowsNotSupported);
}
checkSignatures = !((bool)xamlProperty.GetValue(null, null));
}
string dependentWorkflowAssemblyPath = string.Empty;
@@ -174,8 +172,10 @@ namespace Microsoft.PowerShell.Commands
WriteError(er);
Tracer.TraceErrorRecord(er);
continue;
}
CheckFileSignatureAsNeeded(checkSignatures, resolvedPath);
try
{
// Finally load the file. If there is an access violation, write a
@@ -251,6 +251,8 @@ namespace Microsoft.PowerShell.Commands
continue;
}
CheckFileSignatureAsNeeded(checkSignatures, resolvedPath);
FunctionDetails detailsToUseForUpdate = null;
try
{
@@ -302,7 +304,8 @@ namespace Microsoft.PowerShell.Commands
requiredAssemblies,
dependentWorkflowContent.ToArray(),
dependentWorkflowAssemblyPath,
resolvedPath);
resolvedPath,
this.SessionState.LanguageMode);
// check if the function cache already has the entry to this file
// If detailsToUseForUpdate is not null it is a forced import module
@@ -342,6 +345,14 @@ namespace Microsoft.PowerShell.Commands
} //ProcessRecord
private static void CheckFileSignatureAsNeeded(bool checkSignatures, string filePath)
{
if (checkSignatures && !System.Management.Automation.Internal.SecuritySupport.IsProductBinary(filePath))
{
throw new NotSupportedException(Resources.XamlWorkflowsNotSupported);
}
}
private static readonly ConcurrentDictionary<string, FunctionDetails> FunctionCache =
new ConcurrentDictionary<string, FunctionDetails>(StringComparer.OrdinalIgnoreCase);
@@ -358,7 +369,15 @@ namespace Microsoft.PowerShell.Commands
/// <param name="dependentWorkflows">Any workflows required by this workflow.</param>
/// <param name="dependentAssemblyPath">Path to the dependent assembly.</param>
/// <param name="resolvedPath">Resolved Path of the xaml</param>
private static FunctionDetails GenerateFunctionFromXaml(string name, string xaml, Dictionary<string, string> requiredAssemblies, string[] dependentWorkflows, string dependentAssemblyPath, string resolvedPath)
/// <param name="sourceLanguageMode">Language mode of source in which workflow should run</param>
private static FunctionDetails GenerateFunctionFromXaml(
string name,
string xaml,
Dictionary<string, string> requiredAssemblies,
string[] dependentWorkflows,
string dependentAssemblyPath,
string resolvedPath,
PSLanguageMode sourceLanguageMode)
{
if (name == null)
{
@@ -368,7 +387,21 @@ namespace Microsoft.PowerShell.Commands
}
string modulePath = System.IO.Path.GetDirectoryName(resolvedPath);
string functionDefinition = CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, null, modulePath, false, "[CmdletBinding()]");
string functionDefinition = CreateFunctionFromXaml(
name,
xaml,
requiredAssemblies,
dependentWorkflows,
dependentAssemblyPath,
null,
modulePath,
false,
"[CmdletBinding()]",
null, /* scriptContent */
null, /* fullScript */
null, /* rootWorkflowName */
sourceLanguageMode,
null);
FunctionDetails details = new FunctionDetails
{Name = name, FunctionDefinition = functionDefinition, Xaml = xaml};
@@ -438,6 +471,24 @@ namespace Microsoft.PowerShell.Commands
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "workFlowDefinition")]
public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync,
bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive)
{
return StartWorkflowApplication(command, jobName, workflowGuid, startAsync, parameterCollectionProcessed, parameters, false, null);
}
/// <summary>
/// Executes an instance of the workflow object graph identified by the passed
/// GUID, binding parameters from the Parameters hastable.
/// </summary>
/// <param name="command">The powershell command.</param>
/// <param name="workflowGuid">The GUID used to identify the workflow to run.</param>
/// <param name="parameters">The parameters to pass to the workflow instance.</param>
/// <param name="jobName">The friendly name for the job</param>
/// <param name="parameterCollectionProcessed">True if there was a PSParameters collection</param>
/// <param name="startAsync"></param>
/// <param name="debuggerActive">True if debugger is in active state.</param>
/// <param name="SourceLanguageMode">Language mode of source creating workflow.</param>
public static ContainerParentJob StartWorkflowApplication(PSCmdlet command, string jobName, string workflowGuid, bool startAsync,
bool parameterCollectionProcessed, Hashtable[] parameters, bool debuggerActive, string SourceLanguageMode)
{
Guid trackingGuid = Guid.NewGuid();
_structuredTracer.BeginStartWorkflowApplication(trackingGuid);
@@ -573,6 +624,15 @@ namespace Microsoft.PowerShell.Commands
myJob = command.JobManager.NewJob(specification) as ContainerParentJob;
_structuredTracer.EndCreateNewJob(trackingGuid);
// Pass the source language mode to the workflow job so that it can be
// applied during activity execution.
PSLanguageMode sourceLanguageModeValue;
PSLanguageMode? sourceLanguageMode = null;
if (!string.IsNullOrEmpty(SourceLanguageMode) && Enum.TryParse<PSLanguageMode>(SourceLanguageMode, out sourceLanguageModeValue))
{
sourceLanguageMode = sourceLanguageModeValue;
}
// Raise engine event of new WF job start for debugger, if
// debugger is active (i.e., has breakpoints set).
if (debuggerActive)
@@ -582,12 +642,13 @@ namespace Microsoft.PowerShell.Commands
if (startAsync)
{
if (!PSSessionConfigurationData.IsServerManager)
foreach(PSWorkflowJob childjob in myJob.ChildJobs)
{
foreach(PSWorkflowJob childjob in myJob.ChildJobs)
if (!PSSessionConfigurationData.IsServerManager)
{
childjob.EnableStreamUnloadOnPersistentState();
}
childjob.SourceLanguageMode = sourceLanguageMode;
}
myJob.StartJobAsync();
}
@@ -598,6 +659,7 @@ namespace Microsoft.PowerShell.Commands
foreach (PSWorkflowJob childJob in myJob.ChildJobs)
{
childJob.SynchronousExecution = true;
childJob.SourceLanguageMode = sourceLanguageMode;
}
myJob.StartJob();
}
@@ -605,8 +667,8 @@ namespace Microsoft.PowerShell.Commands
// write an event specifying that job creation is done
_structuredTracer.EndStartWorkflowApplication(trackingGuid);
_structuredTracer.TrackingGuidContainerParentJobCorrelation(trackingGuid, myJob.InstanceId);
return myJob;
return myJob;
}
private static void RaiseWFJobEvent(PSCmdlet command, ContainerParentJob job, bool startAsync)
@@ -1010,6 +1072,46 @@ namespace Microsoft.PowerShell.Commands
string fullScript,
string rootWorkflowName
)
{
return CreateFunctionFromXaml(name, xaml, requiredAssemblies, dependentWorkflows, dependentAssemblyPath, parameterValidation, modulePath,
scriptWorkflow, workflowAttributes, scriptContent, fullScript, rootWorkflowName, null, null);
}
/// <summary>
/// Creates a workflow activity based on the provided Xaml and returns PowerShell script that will
/// run the workflow.
/// </summary>
/// <param name="name">Workflow name</param>
/// <param name="xaml">Workflow Xaml definition</param>
/// <param name="requiredAssemblies">Required assemblies</param>
/// <param name="dependentWorkflows">Dependent workflows</param>
/// <param name="dependentAssemblyPath">Path for dependent assemblies</param>
/// <param name="parameterValidation">Workflow parameters</param>
/// <param name="modulePath">Module path</param>
/// <param name="scriptWorkflow">True if this is script workflow</param>
/// <param name="workflowAttributes">the attribute string to use for the workflow, should be '[CmdletBinding()]'</param>
/// <param name="scriptContent">File path containing script content.</param>
/// <param name="fullScript">Full source script.</param>
/// <param name="rootWorkflowName">Only root Workflow will be compiled</param>
/// <param name="sourceLanguageMode">Language mode of source that is creating the workflow</param>
/// <param name="attributeAstCollection">Optional collection of parameter attribute Asts</param>
/// <returns></returns>
public static string CreateFunctionFromXaml(
string name,
string xaml,
Dictionary<string, string> requiredAssemblies,
string[] dependentWorkflows,
string dependentAssemblyPath,
Dictionary<string, ParameterAst> parameterValidation,
string modulePath,
bool scriptWorkflow,
string workflowAttributes,
string scriptContent,
string fullScript,
string rootWorkflowName,
PSLanguageMode? sourceLanguageMode,
ReadOnlyCollection<AttributeAst> attributeAstCollection
)
{
// check to see if the specified name is allowed
if (!Regex.IsMatch(name, functionNamePattern))
@@ -1304,9 +1406,15 @@ namespace Microsoft.PowerShell.Commands
completeFunctionDefinitionTemplate.AppendLine(" }}");
completeFunctionDefinitionTemplate.AppendLine(FunctionBodyTemplate);
// Mark the function definition with sourceLanguageMode (language mode that function can run under, i.e.,
// as trusted or not trusted), unless the workflow script is marked with the "SecurityCritical" attribute in
// which case the function will always be run under the current system lock down setting.
bool isSecurityCritical = ContainsSecurityCriticalAttribute(attributeAstCollection);
string sourceLanguageModeStr = (!isSecurityCritical && (sourceLanguageMode != null)) ? sourceLanguageMode.ToString() : string.Empty;
// Combine the pieces to create the complete function
string functionDefinition = String.Format(CultureInfo.InvariantCulture, completeFunctionDefinitionTemplate.ToString(),
defaultUpdates, workflowGuid, modulePath);
defaultUpdates, workflowGuid, modulePath, sourceLanguageModeStr);
#if DEBUG
// Verify that the generated function is valid powershell. This is only an issue when changing the
@@ -1334,6 +1442,23 @@ namespace Microsoft.PowerShell.Commands
return functionDefinition;
}
private static Type securityCriticalAttributeType = typeof(System.Security.SecurityCriticalAttribute);
private static bool ContainsSecurityCriticalAttribute(ReadOnlyCollection<AttributeAst> attributeAsts)
{
if (attributeAsts != null)
{
foreach (var attributeAst in attributeAsts)
{
if (attributeAst.TypeName.GetReflectionAttributeType() == securityCriticalAttributeType)
{
return true;
}
}
}
return false;
}
/// <summary>
///
@@ -1515,6 +1640,9 @@ namespace Microsoft.PowerShell.Commands
# which uses this as a base path to find localized content files.
$psBoundParameters['" + Constants.ModulePath + @"'] = '{2}'
# Variable that contains the source language mode.
[string] $SourceLanguageMode = '{3}'
if (Test-Path variable:\PSSenderInfo)
{{
$psBoundParameters['" + Constants.PSSenderInfo + @"'] = $PSSenderInfo
@@ -1693,7 +1821,8 @@ namespace Microsoft.PowerShell.Commands
$AsJob,
$parameterCollectionProcessed,
$finalParameterCollection,
$debuggerActive)
$debuggerActive,
$SourceLanguageMode)
}}
catch
{{
@@ -80,23 +80,29 @@ namespace Microsoft.PowerShell.Workflow
return EndGetRunspace(asyncResult);
}
private Runspace AssignRunspaceIfPossible()
private Runspace AssignRunspaceIfPossible(PSLanguageMode? sourceLanguageMode = null)
{
Runspace runspace = null;
PSLanguageMode languageMode = (sourceLanguageMode != null) ? sourceLanguageMode.Value :
(_languageMode != null) ? _languageMode.Value : GetSystemLanguageMode();
lock (_runspaceCache.TimerServicingSyncObject)
{
// Retrieve or create a local runspace having the same language mode as the source, if provided.
foreach (Item<Runspace> item in _runspaceCache.Cast<Item<Runspace>>().Where(item => !item.Busy))
{
item.Idle = false;
item.Busy = true;
runspace = item.Value;
break;
if (item.Value.SessionStateProxy.LanguageMode == languageMode)
{
item.Idle = false;
item.Busy = true;
runspace = item.Value;
break;
}
}
if ((runspace == null || runspace.RunspaceStateInfo.State != RunspaceState.Opened) &&
(_maxRunspaces == MaxRunspacesPossible || _runspaceCache.Cache.Count < _maxRunspaces))
{
runspace = CreateLocalActivityRunspace(_languageMode);
runspace = CreateLocalActivityRunspace(languageMode);
runspace.Open();
_tracer.WriteMessage("New local runspace created");
@@ -108,6 +114,11 @@ namespace Microsoft.PowerShell.Workflow
return runspace;
}
private static PSLanguageMode GetSystemLanguageMode()
{
return (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) ?
PSLanguageMode.ConstrainedLanguage : PSLanguageMode.FullLanguage;
}
private void TraceThreadPoolInfo(string message)
{
@@ -208,7 +219,23 @@ namespace Microsoft.PowerShell.Workflow
LocalRunspaceAsyncResult asyncResult = new LocalRunspaceAsyncResult(state, callback, Guid.Empty);
Runspace runspace = AssignRunspaceIfPossible();
// Get the source language mode from the activity arguments if available and pass to runspace fetching.
PSLanguageMode? sourceLanguageMode = null;
RunCommandsArguments args = state as RunCommandsArguments;
if (args != null)
{
PSWorkflowRuntime wfRuntime = args.WorkflowHost as PSWorkflowRuntime;
if (wfRuntime != null)
{
PSWorkflowJob wfJob = wfRuntime.JobManager.GetJob(args.PSActivityContext.JobInstanceId);
if (wfJob != null)
{
sourceLanguageMode = wfJob.SourceLanguageMode;
}
}
}
Runspace runspace = AssignRunspaceIfPossible(sourceLanguageMode);
if (runspace != null)
{
asyncResult.Runspace = runspace;
@@ -1205,6 +1205,8 @@ namespace Microsoft.PowerShell.Workflow
internal bool? IsSuspendable = null;
internal PSLanguageMode? SourceLanguageMode { get; set; }
#endregion Internal Accessors
#region Internal Methods
@@ -413,7 +413,7 @@ For more information about how to add checkpoints, see the help topics for Windo
<comment>{0} is a number and should not be localized.</comment>
</data>
<data name="XamlWorkflowsNotSupported" xml:space="preserve">
<value>Cannot load workflow. XAML-based workflows are not supported in this language mode. Only script-based workflows are supported.</value>
<value>Cannot load the workflow. Only signed in-box XAML-based workflows or script-based workflows are supported in the current language mode.</value>
</data>
<data name="RetryingConnection" xml:space="preserve">
<value>Retrying activity connection: attempt {0} of {1}.</value>