MegaBites-AI/Windows-powershell
0372
1// Copyright (c) Microsoft Corporation.2// Licensed under the MIT License.3 4using System;5using System.Collections.ObjectModel;6using System.Diagnostics.CodeAnalysis;7using System.IO;8using System.Management.Automation;9using System.Management.Automation.Internal;10using System.Security.Cryptography.X509Certificates;11 12using Dbg = System.Management.Automation.Diagnostics;13 14namespace Microsoft.PowerShell.Commands15{16 /// <summary>17 /// Defines the base class from which all signature commands18 /// are derived.19 /// </summary>20 public abstract class SignatureCommandsBase : PSCmdlet21 {22 /// <summary>23 /// Gets or sets the path to the file for which to get or set the24 /// digital signature.25 /// </summary>26 [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")]27 public string[] FilePath28 {29 get30 {31 return _path;32 }33 34 set35 {36 _path = value;37 }38 }39 40 private string[] _path;41 42 /// <summary>43 /// Gets or sets the literal path to the file for which to get or set the44 /// digital signature.45 /// </summary>46 [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByLiteralPath")]47 [Alias("PSPath", "LP")]48 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]49 public string[] LiteralPath50 {51 get52 {53 return _path;54 }55 56 set57 {58 _path = value;59 _isLiteralPath = true;60 }61 }62 63 private bool _isLiteralPath = false;64 65 /// <summary>66 /// Gets or sets the digital signature to be written to67 /// the output pipeline.68 /// </summary>69 protected Signature Signature70 {71 get { return _signature; }72 73 set { _signature = value; }74 }75 76 private Signature _signature;77 78 /// <summary>79 /// Gets or sets the file type of the byte array containing the content with80 /// digital signature.81 /// </summary>82 [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByContent")]83 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]84 public string[] SourcePathOrExtension85 {86 get87 {88 return _sourcePathOrExtension;89 }90 91 set92 {93 _sourcePathOrExtension = value;94 }95 }96 97 private string[] _sourcePathOrExtension;98 99 /// <summary>100 /// File contents as a byte array.101 /// </summary>102 [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByContent")]103 [ValidateNotNullOrEmpty]104 [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")]105 public byte[] Content106 {107 get108 {109 return _content;110 }111 112 set113 {114 _content = value;115 }116 }117 118 private byte[] _content;119 120 //121 // name of this command122 //123 private readonly string _commandName;124 125 /// <summary>126 /// Initializes a new instance of the SignatureCommandsBase class,127 /// using the given command name.128 /// </summary>129 /// <param name="name">130 /// The name of the command.131 /// </param>132 protected SignatureCommandsBase(string name) : base()133 {134 _commandName = name;135 }136 137 //138 // hide default ctor139 //140 private SignatureCommandsBase() : base() { }141 142 /// <summary>143 /// Processes records from the input pipeline.144 /// For each input object, the command gets or145 /// sets the digital signature on the object, and146 /// and exports the object.147 /// </summary>148 protected override void ProcessRecord()149 {150 if (Content == null)151 {152 //153 // this cannot happen as we have specified the Path154 // property to be mandatory parameter155 //156 Dbg.Assert((FilePath != null) && (FilePath.Length > 0),157 "GetSignatureCommand: Param binder did not bind path");158 159 foreach (string p in FilePath)160 {161 Collection<string> paths = new();162 163 // Expand wildcard characters164 if (_isLiteralPath)165 {166 paths.Add(SessionState.Path.GetUnresolvedProviderPathFromPSPath(p));167 }168 else169 {170 try171 {172 foreach (PathInfo tempPath in SessionState.Path.GetResolvedPSPathFromPSPath(p))173 {174 paths.Add(tempPath.ProviderPath);175 }176 }177 catch (ItemNotFoundException)178 {179 WriteError(180 SecurityUtils.CreateFileNotFoundErrorRecord(181 SignatureCommands.FileNotFound,182 "SignatureCommandsBaseFileNotFound", p));183 }184 }185 186 if (paths.Count == 0)187 continue;188 189 bool foundFile = false;190 191 foreach (string path in paths)192 {193 if (!System.IO.Directory.Exists(path))194 {195 foundFile = true;196 197 string resolvedFilePath = SecurityUtils.GetFilePathOfExistingFile(this, path);198 199 if (resolvedFilePath == null)200 {201 WriteError(SecurityUtils.CreateFileNotFoundErrorRecord(202 SignatureCommands.FileNotFound,203 "SignatureCommandsBaseFileNotFound",204 path));205 }206 else207 {208 if ((Signature = PerformAction(resolvedFilePath)) != null)209 {210 WriteObject(Signature);211 }212 }213 }214 }215 216 if (!foundFile)217 {218 WriteError(SecurityUtils.CreateFileNotFoundErrorRecord(219 SignatureCommands.CannotRetrieveFromContainer,220 "SignatureCommandsBaseCannotRetrieveFromContainer"));221 }222 }223 }224 else225 {226 foreach (string sourcePathOrExtension in SourcePathOrExtension)227 {228 if ((Signature = PerformAction(sourcePathOrExtension, Content)) != null)229 {230 WriteObject(Signature);231 }232 }233 }234 }235 236 /// <summary>237 /// Performs the action (ie: get signature, or set signature)238 /// on the specified file.239 /// </summary>240 /// <param name="filePath">241 /// The name of the file on which to perform the action.242 /// </param>243 protected abstract Signature PerformAction(string filePath);244 245 /// <summary>246 /// Performs the action (ie: get signature, or set signature)247 /// on the specified contents.248 /// </summary>249 /// <param name="fileName">250 /// The filename used for type if content is specified.251 /// </param>252 /// <param name="content">253 /// The file contents on which to perform the action.254 /// </param>255 protected abstract Signature PerformAction(string fileName, byte[] content);256 }257 258 /// <summary>259 /// Defines the implementation of the 'get-AuthenticodeSignature' cmdlet.260 /// This cmdlet extracts the digital signature from the given file.261 /// </summary>262 [Cmdlet(VerbsCommon.Get, "AuthenticodeSignature", DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096823")]263 [OutputType(typeof(Signature))]264 public sealed class GetAuthenticodeSignatureCommand : SignatureCommandsBase265 {266 /// <summary>267 /// Initializes a new instance of the GetSignatureCommand class.268 /// </summary>269 public GetAuthenticodeSignatureCommand() : base("Get-AuthenticodeSignature") { }270 271 /// <summary>272 /// Gets the signature from the specified file.273 /// </summary>274 /// <param name="filePath">275 /// The name of the file on which to perform the action.276 /// </param>277 /// <returns>278 /// The signature on the specified file.279 /// </returns>280 protected override Signature PerformAction(string filePath)281 {282 return SignatureHelper.GetSignature(filePath, null);283 }284 285 /// <summary>286 /// Gets the signature from the specified file contents.287 /// </summary>288 /// <param name="sourcePathOrExtension">The file type associated with the contents.</param>289 /// <param name="content">290 /// The contents of the file on which to perform the action.291 /// </param>292 /// <returns>293 /// The signature on the specified file contents.294 /// </returns>295 protected override Signature PerformAction(string sourcePathOrExtension, byte[] content)296 {297 return SignatureHelper.GetSignature(sourcePathOrExtension, content);298 }299 }300 301 /// <summary>302 /// Defines the implementation of the 'set-AuthenticodeSignature' cmdlet.303 /// This cmdlet sets the digital signature on a given file.304 /// </summary>305 [Cmdlet(VerbsCommon.Set, "AuthenticodeSignature", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath",306 HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096919")]307 [OutputType(typeof(Signature))]308 public sealed class SetAuthenticodeSignatureCommand : SignatureCommandsBase309 {310 /// <summary>311 /// Initializes a new instance of the SetAuthenticodeSignatureCommand class.312 /// </summary>313 public SetAuthenticodeSignatureCommand() : base("set-AuthenticodeSignature") { }314 315 /// <summary>316 /// Gets or sets the certificate with which to sign the317 /// file.318 /// </summary>319 [Parameter(Position = 1, Mandatory = true)]320 public X509Certificate2 Certificate321 {322 get323 {324 return _certificate;325 }326 327 set328 {329 _certificate = value;330 }331 }332 333 private X509Certificate2 _certificate;334 335 /// <summary>336 /// Gets or sets the additional certificates to337 /// include in the digital signature.338 /// Use 'signer' to include only the signer's certificate.339 /// Use 'notroot' to include all certificates in the certificate340 /// chain, except for the root authority.341 /// Use 'all' to include all certificates in the certificate chain.342 ///343 /// Defaults to 'notroot'.344 /// </summary>345 [Parameter(Mandatory = false)]346 [ValidateSet("signer", "notroot", "all")]347 public string IncludeChain348 {349 get350 {351 return _includeChain;352 }353 354 set355 {356 _includeChain = value;357 }358 }359 360 private string _includeChain = "notroot";361 362 /// <summary>363 /// Gets or sets the Url of the time stamping server.364 /// The time stamping server certifies the exact time365 /// that the certificate was added to the file.366 /// </summary>367 [Parameter(Mandatory = false)]368 public string TimestampServer369 {370 get371 {372 return _timestampServer;373 }374 375 set376 {377 value ??= string.Empty;378 379 _timestampServer = value;380 }381 }382 383 private string _timestampServer = string.Empty;384 385 /// <summary>386 /// Gets or sets the hash algorithm used for signing.387 /// This string value must represent the name of a Cryptographic Algorithm388 /// Identifier supported by Windows.389 /// </summary>390 [Parameter(Mandatory = false)]391 public string HashAlgorithm392 {393 get394 {395 return _hashAlgorithm;396 }397 398 set399 {400 _hashAlgorithm = value;401 }402 }403 404 private string _hashAlgorithm = "SHA256";405 406 /// <summary>407 /// Property that sets force parameter.408 /// </summary>409 [Parameter]410 public SwitchParameter Force411 {412 get413 {414 return _force;415 }416 417 set418 {419 _force = value;420 }421 }422 423 private bool _force;424 425 /// <summary>426 /// Sets the digital signature on the specified file.427 /// </summary>428 /// <param name="filePath">429 /// The name of the file on which to perform the action.430 /// </param>431 /// <returns>432 /// The signature on the specified file.433 /// </returns>434 protected override Signature PerformAction(string filePath)435 {436 SigningOption option = GetSigningOption(IncludeChain);437 438 if (Certificate == null)439 {440 throw PSTraceSource.NewArgumentNullException("certificate");441 }442 443 //444 // if the cert is not good for signing, we cannot445 // process any more files. Exit the command.446 //447 if (!SecuritySupport.CertIsGoodForSigning(Certificate))448 {449 Exception e = PSTraceSource.NewArgumentException(450 "certificate",451 SignatureCommands.CertNotGoodForSigning);452 453 throw e;454 }455 456 if (!ShouldProcess(filePath))457 return null;458 459 FileInfo readOnlyFileInfo = null;460 try461 {462 if (this.Force)463 {464 try465 {466 // remove readonly attributes on the file467 FileInfo fInfo = new(filePath);468 if (fInfo != null)469 {470 // Save some disk write time by checking whether file is readonly..471 if ((fInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly)472 {473 // remember to reset the read-only attribute later474 readOnlyFileInfo = fInfo;475 // Make sure the file is not read only476 fInfo.Attributes &= ~(FileAttributes.ReadOnly);477 }478 }479 }480 // These are the known exceptions for File.Load and StreamWriter.ctor481 catch (ArgumentException e)482 {483 ErrorRecord er = new(484 e,485 "ForceArgumentException",486 ErrorCategory.WriteError,487 filePath);488 WriteError(er);489 return null;490 }491 catch (IOException e)492 {493 ErrorRecord er = new(494 e,495 "ForceIOException",496 ErrorCategory.WriteError,497 filePath);498 WriteError(er);499 return null;500 }501 catch (UnauthorizedAccessException e)502 {503 ErrorRecord er = new(504 e,505 "ForceUnauthorizedAccessException",506 ErrorCategory.PermissionDenied,507 filePath);508 WriteError(er);509 return null;510 }511 catch (NotSupportedException e)512 {513 ErrorRecord er = new(514 e,515 "ForceNotSupportedException",516 ErrorCategory.WriteError,517 filePath);518 WriteError(er);519 return null;520 }521 catch (System.Security.SecurityException e)522 {523 ErrorRecord er = new(524 e,525 "ForceSecurityException",526 ErrorCategory.PermissionDenied,527 filePath);528 WriteError(er);529 return null;530 }531 }532 533 //534 // ProcessRecord() code in base class has already535 // ascertained that filePath really represents an existing536 // file. Thus we can safely call GetFileSize() below.537 //538 539 if (SecurityUtils.GetFileSize(filePath) < 4)540 {541 // Note that the message param comes first542 string message = string.Format(543 System.Globalization.CultureInfo.CurrentCulture,544 UtilsStrings.FileSmallerThan4Bytes, filePath);545 546 PSArgumentException e = new(message, nameof(filePath));547 ErrorRecord er = SecurityUtils.CreateInvalidArgumentErrorRecord(548 e,549 "SignatureCommandsBaseFileSmallerThan4Bytes"550 );551 552 WriteError(er);553 554 return null;555 }556 557 return SignatureHelper.SignFile(option,558 filePath,559 Certificate,560 TimestampServer,561 _hashAlgorithm);562 }563 finally564 {565 // reset the read-only attribute566 if (readOnlyFileInfo != null)567 {568 readOnlyFileInfo.Attributes |= FileAttributes.ReadOnly;569 }570 }571 }572 573 /// <summary>574 /// Not implemented.575 /// </summary>576 protected override Signature PerformAction(string sourcePathOrExtension, byte[] content)577 {578 throw new NotImplementedException();579 }580 581 private struct SigningOptionInfo582 {583 internal SigningOption option;584 internal string optionName;585 586 internal SigningOptionInfo(SigningOption o, string n)587 {588 option = o;589 optionName = n;590 }591 }592 593 /// <summary>594 /// Association between SigningOption.* values and the595 /// corresponding string names.596 /// </summary>597 private static readonly SigningOptionInfo[] s_sigOptionInfo =598 {599 new SigningOptionInfo(SigningOption.AddOnlyCertificate, "signer"),600 new SigningOptionInfo(SigningOption.AddFullCertificateChainExceptRoot, "notroot"),601 new SigningOptionInfo(SigningOption.AddFullCertificateChain, "all")602 };603 604 /// <summary>605 /// Get SigningOption value corresponding to a string name.606 /// </summary>607 /// <param name="optionName">Name of option.</param>608 /// <returns>SigningOption.</returns>609 private static SigningOption GetSigningOption(string optionName)610 {611 foreach (SigningOptionInfo si in s_sigOptionInfo)612 {613 if (string.Equals(optionName, si.optionName,614 StringComparison.OrdinalIgnoreCase))615 {616 return si.option;617 }618 }619 620 return SigningOption.AddFullCertificateChainExceptRoot;621 }622 }623}624 