Team Ai
Datasetpublic

MegaBites-AI/Windows-powershell

sourceHugging Facemitupdated 6mo agoView on Hugging Face
0likes372downloads
CmsCommands.cs603 linesDownload Raw Back to security
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.Generic;6using System.Collections.ObjectModel;7using System.Globalization;8using System.Management.Automation;9using System.Security.Cryptography.Pkcs;10using System.Security.Cryptography.X509Certificates;11using System.Text;12 13namespace Microsoft.PowerShell.Commands14{15    /// <summary>16    /// Defines the implementation of the 'Protect-CmsMessage' cmdlet.17    ///18    /// This cmdlet generates a new encrypted CMS message given the19    /// recipient and content supplied.20    /// </summary>21    [Cmdlet(VerbsSecurity.Protect, "CmsMessage", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096826", DefaultParameterSetName = "ByContent")]22    [OutputType(typeof(string))]23    public sealed class ProtectCmsMessageCommand : PSCmdlet24    {25        /// <summary>26        /// Gets or sets the recipient of the CMS Message.27        /// </summary>28        [Parameter(Position = 0, Mandatory = true)]29        public CmsMessageRecipient[] To30        {31            get; set;32        }33 34        /// <summary>35        /// Gets or sets the content of the CMS Message.36        /// </summary>37        [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true, ParameterSetName = "ByContent")]38        [AllowNull]39        [AllowEmptyString]40        public PSObject Content41        {42            get;43            set;44        }45 46        private readonly PSDataCollection<PSObject> _inputObjects = new();47 48        /// <summary>49        /// Gets or sets the content of the CMS Message by path.50        /// </summary>51        [Parameter(Position = 1, Mandatory = true, ParameterSetName = "ByPath")]52        public string Path53        {54            get;55            set;56        }57 58        /// <summary>59        /// Gets or sets the content of the CMS Message by literal path.60        /// </summary>61        [Parameter(Position = 1, Mandatory = true, ParameterSetName = "ByLiteralPath")]62        public string LiteralPath63        {64            get;65            set;66        }67 68        private string _resolvedPath = null;69 70        /// <summary>71        /// Emits the protected message to a file path.72        /// </summary>73        [Parameter(Position = 2)]74        public string OutFile75        {76            get;77            set;78        }79 80        private string _resolvedOutFile = null;81 82        /// <summary>83        /// Validate / convert arguments.84        /// </summary>85        protected override void BeginProcessing()86        {87            // Validate Path88            if (!string.IsNullOrEmpty(Path))89            {90                ProviderInfo provider = null;91                Collection<string> resolvedPaths = GetResolvedProviderPathFromPSPath(Path, out provider);92 93                // Ensure the path is a single path from the file system provider94                if ((resolvedPaths.Count > 1) ||95                    (!string.Equals(provider.Name, "FileSystem", StringComparison.OrdinalIgnoreCase)))96                {97                    ErrorRecord error = new(98                        new ArgumentException(99                            string.Format(100                                CultureInfo.InvariantCulture,101                                CmsCommands.FilePathMustBeFileSystemPath,102                                Path)),103                        "FilePathMustBeFileSystemPath",104                        ErrorCategory.ObjectNotFound,105                        provider);106                    ThrowTerminatingError(error);107                }108 109                _resolvedPath = resolvedPaths[0];110            }111 112            if (!string.IsNullOrEmpty(LiteralPath))113            {114                // Validate that the path exists115                SessionState.InvokeProvider.Item.Get(new string[] { LiteralPath }, false, true);116                _resolvedPath = LiteralPath;117            }118 119            // Validate OutFile120            if (!string.IsNullOrEmpty(OutFile))121            {122                _resolvedOutFile = GetUnresolvedProviderPathFromPSPath(OutFile);123            }124        }125 126        /// <summary>127        /// Processes records from the input pipeline.128        /// For each input object, the command encrypts129        /// and exports the object.130        /// </summary>131        protected override void ProcessRecord()132        {133            if (string.Equals("ByContent", this.ParameterSetName, StringComparison.OrdinalIgnoreCase))134            {135                _inputObjects.Add(Content);136            }137        }138 139        /// <summary>140        /// Encrypts and outputs the message.141        /// </summary>142        protected override void EndProcessing()143        {144            byte[] contentBytes = null;145 146            if (_inputObjects.Count > 0)147            {148                StringBuilder outputString = new();149 150                Collection<PSObject> output = System.Management.Automation.PowerShell.Create()151                    .AddCommand("Microsoft.PowerShell.Utility\\Out-String")152                    .AddParameter("Stream")153                    .Invoke(_inputObjects);154 155                foreach (PSObject outputObject in output)156                {157                    if (outputString.Length > 0)158                    {159                        outputString.AppendLine();160                    }161 162                    outputString.Append(outputObject);163                }164 165                contentBytes = System.Text.Encoding.UTF8.GetBytes(outputString.ToString());166            }167            else168            {169                contentBytes = System.IO.File.ReadAllBytes(_resolvedPath);170            }171 172            ErrorRecord terminatingError = null;173            string encodedContent = CmsUtils.Encrypt(contentBytes, To, this.SessionState, out terminatingError);174 175            if (terminatingError != null)176            {177                ThrowTerminatingError(terminatingError);178            }179 180            if (string.IsNullOrEmpty(_resolvedOutFile))181            {182                WriteObject(encodedContent);183            }184            else185            {186                System.IO.File.WriteAllText(_resolvedOutFile, encodedContent);187            }188        }189    }190 191    /// <summary>192    /// Defines the implementation of the 'Get-CmsMessage' cmdlet.193    ///194    /// This cmdlet retrieves information about an encrypted CMS195    /// message.196    /// </summary>197    [Cmdlet(VerbsCommon.Get, "CmsMessage", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096598")]198    [OutputType(typeof(EnvelopedCms))]199    public sealed class GetCmsMessageCommand : PSCmdlet200    {201        /// <summary>202        /// Gets or sets the content of the CMS Message.203        /// </summary>204        [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ParameterSetName = "ByContent")]205        [AllowNull]206        [AllowEmptyString]207        public string Content208        {209            get;210            set;211        }212 213        private readonly StringBuilder _contentBuffer = new();214 215        /// <summary>216        /// Gets or sets the CMS Message by path.217        /// </summary>218        [Parameter(Position = 1, Mandatory = true, ParameterSetName = "ByPath")]219        public string Path220        {221            get;222            set;223        }224 225        /// <summary>226        /// Gets or sets the CMS Message by literal path.227        /// </summary>228        [Parameter(Position = 1, Mandatory = true, ParameterSetName = "ByLiteralPath")]229        public string LiteralPath230        {231            get;232            set;233        }234 235        private string _resolvedPath = null;236 237        /// <summary>238        /// Validate / convert arguments.239        /// </summary>240        protected override void BeginProcessing()241        {242            // Validate Path243            if (!string.IsNullOrEmpty(Path))244            {245                ProviderInfo provider = null;246                Collection<string> resolvedPaths = GetResolvedProviderPathFromPSPath(Path, out provider);247 248                // Ensure the path is a single path from the file system provider249                if ((resolvedPaths.Count > 1) ||250                    (!string.Equals(provider.Name, "FileSystem", StringComparison.OrdinalIgnoreCase)))251                {252                    ErrorRecord error = new(253                        new ArgumentException(254                            string.Format(255                                CultureInfo.InvariantCulture,256                                CmsCommands.FilePathMustBeFileSystemPath,257                                Path)),258                        "FilePathMustBeFileSystemPath",259                        ErrorCategory.ObjectNotFound,260                        provider);261                    ThrowTerminatingError(error);262                }263 264                _resolvedPath = resolvedPaths[0];265            }266 267            if (!string.IsNullOrEmpty(LiteralPath))268            {269                // Validate that the path exists270                SessionState.InvokeProvider.Item.Get(new string[] { LiteralPath }, false, true);271                _resolvedPath = LiteralPath;272            }273        }274 275        /// <summary>276        /// Processes records from the input pipeline.277        /// For each input object, the command gets the information278        /// about the protected message and exports the object.279        /// </summary>280        protected override void ProcessRecord()281        {282            if (string.Equals("ByContent", this.ParameterSetName, StringComparison.OrdinalIgnoreCase))283            {284                if (_contentBuffer.Length > 0)285                {286                    _contentBuffer.Append(System.Environment.NewLine);287                }288 289                _contentBuffer.Append(Content);290            }291        }292 293        /// <summary>294        /// Gets the CMS Message object.295        /// </summary>296        protected override void EndProcessing()297        {298            string actualContent = null;299 300            // Read in the content301            if (string.Equals("ByContent", this.ParameterSetName, StringComparison.OrdinalIgnoreCase))302            {303                actualContent = _contentBuffer.ToString();304            }305            else306            {307                actualContent = System.IO.File.ReadAllText(_resolvedPath);308            }309 310            // Extract out the bytes and Base64 decode them311            byte[] contentBytes = CmsUtils.RemoveAsciiArmor(actualContent, CmsUtils.BEGIN_CMS_SIGIL, CmsUtils.END_CMS_SIGIL, out int _, out int _);312            if (contentBytes == null)313            {314                ErrorRecord error = new(315                    new ArgumentException(CmsCommands.InputContainedNoEncryptedContent),316                    "InputContainedNoEncryptedContent", ErrorCategory.ObjectNotFound, null);317                ThrowTerminatingError(error);318            }319 320            EnvelopedCms cms = new();321            cms.Decode(contentBytes);322 323            PSObject result = new(cms);324            List<object> recipients = new();325            foreach (RecipientInfo recipient in cms.RecipientInfos)326            {327                recipients.Add(recipient.RecipientIdentifier.Value);328            }329 330            result.Properties.Add(331                new PSNoteProperty("Recipients", recipients));332            result.Properties.Add(333                new PSNoteProperty("Content", actualContent));334 335            WriteObject(result);336        }337    }338 339    /// <summary>340    /// Defines the implementation of the 'Unprotect-CmsMessage' cmdlet.341    ///342    /// This cmdlet retrieves the clear text content of an encrypted CMS343    /// message.344    /// </summary>345    [Cmdlet(VerbsSecurity.Unprotect, "CmsMessage", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096701", DefaultParameterSetName = "ByWinEvent")]346    [OutputType(typeof(string))]347    public sealed class UnprotectCmsMessageCommand : PSCmdlet348    {349        /// <summary>350        /// Gets or sets the content of the CMS Message.351        /// </summary>352        [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByContent")]353        [AllowNull]354        [AllowEmptyString]355        public string Content356        {357            get;358            set;359        }360 361        private readonly StringBuilder _contentBuffer = new();362 363        /// <summary>364        /// Gets or sets the Windows Event Log Message with contents to be decrypted.365        /// </summary>366        [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ParameterSetName = "ByWinEvent")]367        [PSTypeName("System.Diagnostics.Eventing.Reader.EventLogRecord")]368        public PSObject EventLogRecord369        {370            get;371            set;372        }373 374        /// <summary>375        /// Gets or sets the CMS Message by path.376        /// </summary>377        [Parameter(Position = 0, Mandatory = true, ParameterSetName = "ByPath")]378        public string Path379        {380            get;381            set;382        }383 384        /// <summary>385        /// Gets or sets the CMS Message by literal path.386        /// </summary>387        [Parameter(Position = 0, Mandatory = true, ParameterSetName = "ByLiteralPath")]388        public string LiteralPath389        {390            get;391            set;392        }393 394        private string _resolvedPath = null;395 396        /// <summary>397        /// Determines whether to include the decrypted content in its original context,398        /// rather than just output the decrypted content itself.399        /// </summary>400        [Parameter]401        public SwitchParameter IncludeContext402        {403            get;404            set;405        }406 407        /// <summary>408        /// Gets or sets the recipient of the CMS Message.409        /// </summary>410        [Parameter(Position = 1)]411        public CmsMessageRecipient[] To412        {413            get;414            set;415        }416 417        /// <summary>418        /// Validate / convert arguments.419        /// </summary>420        protected override void BeginProcessing()421        {422            // Validate Path423            if (!string.IsNullOrEmpty(Path))424            {425                ProviderInfo provider = null;426                Collection<string> resolvedPaths = GetResolvedProviderPathFromPSPath(Path, out provider);427 428                // Ensure the path is a single path from the file system provider429                if ((resolvedPaths.Count > 1) ||430                    (!string.Equals(provider.Name, "FileSystem", StringComparison.OrdinalIgnoreCase)))431                {432                    ErrorRecord error = new(433                        new ArgumentException(434                            string.Format(435                                CultureInfo.InvariantCulture,436                                CmsCommands.FilePathMustBeFileSystemPath,437                                Path)),438                        "FilePathMustBeFileSystemPath",439                        ErrorCategory.ObjectNotFound,440                        provider);441                    ThrowTerminatingError(error);442                }443 444                _resolvedPath = resolvedPaths[0];445            }446 447            if (!string.IsNullOrEmpty(LiteralPath))448            {449                // Validate that the path exists450                SessionState.InvokeProvider.Item.Get(new string[] { LiteralPath }, false, true);451                _resolvedPath = LiteralPath;452            }453        }454 455        /// <summary>456        /// Processes records from the input pipeline.457        /// For each input object, the command gets the information458        /// about the protected message and exports the object.459        /// </summary>460        protected override void ProcessRecord()461        {462            // If we're process by content, collect it.463            if (string.Equals("ByContent", this.ParameterSetName, StringComparison.OrdinalIgnoreCase))464            {465                if (_contentBuffer.Length > 0)466                {467                    _contentBuffer.Append(System.Environment.NewLine);468                }469 470                _contentBuffer.Append(Content);471            }472 473            // If we're processing event log records, decrypt those inline.474            if (string.Equals("ByWinEvent", this.ParameterSetName, StringComparison.OrdinalIgnoreCase))475            {476                string actualContent = EventLogRecord.Properties["Message"].Value.ToString();477                string decrypted = Decrypt(actualContent);478 479                if (!IncludeContext)480                {481                    WriteObject(decrypted);482                }483                else484                {485                    EventLogRecord.Properties["Message"].Value = decrypted;486                    WriteObject(EventLogRecord);487                }488            }489        }490 491        /// <summary>492        /// Processes records from the input pipeline.493        /// For each input object, the command gets the information494        /// about the protected message and exports the object.495        /// </summary>496        protected override void EndProcessing()497        {498            if (string.Equals("ByWinEvent", this.ParameterSetName, StringComparison.OrdinalIgnoreCase))499            {500                return;501            }502 503            string actualContent = null;504 505            // Read in the content506            if (string.Equals("ByContent", this.ParameterSetName, StringComparison.OrdinalIgnoreCase))507            {508                actualContent = _contentBuffer.ToString();509            }510            else511            {512                actualContent = System.IO.File.ReadAllText(_resolvedPath);513            }514 515            string decrypted = Decrypt(actualContent);516            WriteObject(decrypted);517        }518 519        private string Decrypt(string actualContent)520        {521            // Extract out the bytes and Base64 decode them522            int startIndex, endIndex;523            byte[] messageBytes = CmsUtils.RemoveAsciiArmor(actualContent, CmsUtils.BEGIN_CMS_SIGIL, CmsUtils.END_CMS_SIGIL, out startIndex, out endIndex);524            if ((messageBytes == null) && (!IncludeContext))525            {526                ErrorRecord error = new(527                    new ArgumentException(528                        string.Format(529                            CultureInfo.InvariantCulture,530                            CmsCommands.InputContainedNoEncryptedContentIncludeContext,531                            "-IncludeContext")),532                    "InputContainedNoEncryptedContentIncludeContext",533                    ErrorCategory.ObjectNotFound,534                    targetObject: null);535                ThrowTerminatingError(error);536            }537 538            // Capture the pre and post context, if there was any539            string preContext = null;540            string postContext = null;541            if (IncludeContext)542            {543                if (startIndex > -1)544                {545                    preContext = actualContent.Substring(0, startIndex);546                }547 548                if (endIndex > -1)549                {550                    postContext = actualContent.Substring(endIndex);551                }552            }553 554            EnvelopedCms cms = new();555            X509Certificate2Collection certificates = new();556 557            if ((To != null) && (To.Length > 0))558            {559                ErrorRecord error = null;560 561                foreach (CmsMessageRecipient recipient in To)562                {563                    recipient.Resolve(this.SessionState, ResolutionPurpose.Decryption, out error);564                    if (error != null)565                    {566                        ThrowTerminatingError(error);567                        return null;568                    }569 570                    foreach (X509Certificate2 certificate in recipient.Certificates)571                    {572                        certificates.Add(certificate);573                    }574                }575            }576 577            string resultString = actualContent;578            if (messageBytes != null)579            {580                cms.Decode(messageBytes);581                cms.Decrypt(certificates);582 583                resultString = System.Text.Encoding.UTF8.GetString(cms.ContentInfo.Content);584            }585 586            if (IncludeContext)587            {588                if (preContext != null)589                {590                    resultString = preContext + resultString;591                }592 593                if (postContext != null)594                {595                    resultString += postContext;596                }597            }598 599            return resultString;600        }601    }602}603