Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cherrypick] [release-6.3.x] Signing: enable X.509 trust store on Linux/macOS #4722

Merged
merged 2 commits into from
Jul 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using NuGet.Credentials;
using NuGet.LibraryModel;
using NuGet.Packaging;
using NuGet.Packaging.Signing;
using NuGet.Versioning;

namespace Microsoft.Build.NuGetSdkResolver
Expand Down Expand Up @@ -146,6 +147,10 @@ public static SdkResult GetSdkResult(SdkReference sdk, object nuGetVersion, SdkR
{
DefaultCredentialServiceUtility.SetupDefaultCredentialService(logger, nonInteractive: !context.Interactive);

#if !NETFRAMEWORK
X509TrustStore.InitializeForDotNetSdk(logger);
#endif

// Asynchronously run the restore without a commit which find the package on configured feeds, download, and unzip it without generating any other files
// This must be run in its own task because legacy project system evaluates projects on the UI thread which can cause RunWithoutCommit() to deadlock
// https://developercommunity.visualstudio.com/content/problem/311379/solution-load-never-completes-when-project-contain.html
Expand Down
4 changes: 3 additions & 1 deletion src/NuGet.Core/NuGet.Build.Tasks/BuildTasksUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Credentials;
using NuGet.Packaging.Signing;
using NuGet.ProjectModel;
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
Expand All @@ -25,7 +26,6 @@
using System.Xml.Linq;
using NuGet.Packaging;
using NuGet.Packaging.PackageExtraction;
using NuGet.Packaging.Signing;
using NuGet.PackageManagement;
using NuGet.ProjectManagement;
using NuGet.Shared;
Expand Down Expand Up @@ -191,6 +191,8 @@ public static async Task<bool> RestoreAsync(
UserAgent.SetUserAgentString(new UserAgentStringBuilder("NuGet Desktop MSBuild Task"));
#endif

X509TrustStore.InitializeForDotNetSdk(log);

var restoreSummaries = new List<RestoreSummary>();
var providerCache = new RestoreCommandProvidersCache();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.IO;
using Microsoft.Extensions.CommandLineUtils;
using NuGet.Common;
using NuGet.Packaging.Signing;

namespace NuGet.CommandLine.XPlat
{
Expand Down Expand Up @@ -101,6 +102,9 @@ public static void Register(CommandLineApplication app, Func<ILogger> getLogger,
PackageId = id.Values[0]
};
var msBuild = new MSBuildAPIUtility(logger);

X509TrustStore.InitializeForDotNetSdk(logger);

var addPackageRefCommandRunner = getCommandRunner();
return addPackageRefCommandRunner.ExecuteCommand(packageRefArgs, msBuild);
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,8 @@ internal static void Register(CommandLineApplication app,

setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value()));

X509TrustStore.InitializeForDotNetSdk(args.Logger);

ISignCommandRunner runner = getCommandRunner();
int result = await runner.ExecuteCommandAsync(args);
return result;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Globalization;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using NuGet.Commands;
Expand Down Expand Up @@ -267,6 +266,12 @@ private static async Task<int> ExecuteCommand(TrustCommand action,

setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value()));

// Add is the only action which does certificate chain building.
if (trustedSignersArgs.Action == TrustedSignersAction.Add)
{
X509TrustStore.InitializeForDotNetSdk(logger);
}

#pragma warning disable CS0618 // Type or member is obsolete
var sourceProvider = new PackageSourceProvider(settings, enablePackageSourcesChangedEvent: false);
#pragma warning restore CS0618 // Type or member is obsolete
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using NuGet.Commands;
using NuGet.Common;
using NuGet.Configuration;
using NuGet.Packaging.Signing;
using static NuGet.Commands.VerifyArgs;

namespace NuGet.CommandLine.XPlat
Expand Down Expand Up @@ -64,6 +65,8 @@ internal static void Register(CommandLineApplication app,
args.Settings = XPlatUtility.ProcessConfigFile(configFile.Value());
setLogLevel(XPlatUtility.MSBuildVerbosityToNuGetLogLevel(verbosity.Value()));

X509TrustStore.InitializeForDotNetSdk(args.Logger);

var runner = getCommandRunner();
var verifyTask = runner.ExecuteCommandAsync(args);
await verifyTask;
Expand Down
16 changes: 9 additions & 7 deletions src/NuGet.Core/NuGet.Packaging/PackageArchiveReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -524,19 +524,21 @@ public override bool CanVerifySignedPackages(SignedPackageVerifierSettings verif
// Please note: Linux/MAC case sensitive for env var name.
string signVerifyEnvVariable = _environmentVariableReader.GetEnvironmentVariable("DOTNET_NUGET_SIGNATURE_VERIFICATION");

// Not opt-out option, only opt-in feature.
bool canVerify = false;

if (!string.IsNullOrEmpty(signVerifyEnvVariable))
{
if (signVerifyEnvVariable.Equals(bool.TrueString, StringComparison.OrdinalIgnoreCase))
if (string.Equals(bool.TrueString, signVerifyEnvVariable, StringComparison.OrdinalIgnoreCase))
{
return true;
canVerify = true;
}
else if (string.Equals(bool.FalseString, signVerifyEnvVariable, StringComparison.OrdinalIgnoreCase))
{
canVerify = false;
}

// other values are unsupported
return false;
}

return false;
return canVerify;
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

[assembly: CLSCompliant(true)]

[assembly: InternalsVisibleTo("Dotnet.Integration.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("NuGet.Packaging.FuncTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("NuGet.Packaging.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
[assembly: InternalsVisibleTo("NuGet.Commands.Test, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")]
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
NuGet.Packaging.Licenses.NuGetLicenseExpressionParsingException.NuGetLicenseExpressionParsingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void
NuGet.Packaging.Licenses.NuGetLicenseExpressionParsingException.NuGetLicenseExpressionParsingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void
NuGet.Packaging.Signing.X509TrustStore
static NuGet.Packaging.Signing.X509TrustStore.InitializeForDotNetSdk(NuGet.Common.ILogger logger) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
NuGet.Packaging.Licenses.NuGetLicenseExpressionParsingException.NuGetLicenseExpressionParsingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void
NuGet.Packaging.Licenses.NuGetLicenseExpressionParsingException.NuGetLicenseExpressionParsingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void
NuGet.Packaging.Signing.X509TrustStore
static NuGet.Packaging.Signing.X509TrustStore.InitializeForDotNetSdk(NuGet.Common.ILogger logger) -> void
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
NuGet.Packaging.Licenses.NuGetLicenseExpressionParsingException.NuGetLicenseExpressionParsingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void
NuGet.Packaging.Licenses.NuGetLicenseExpressionParsingException.NuGetLicenseExpressionParsingException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) -> void
NuGet.Packaging.Signing.X509TrustStore
static NuGet.Packaging.Signing.X509TrustStore.InitializeForDotNetSdk(NuGet.Common.ILogger logger) -> void
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#if NET5_0_OR_GREATER

using System;
using System.IO;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;

namespace NuGet.Packaging.Signing
{
internal abstract class CertificateBundleX509ChainFactory : IX509ChainFactory
{
public X509Certificate2Collection Certificates { get; }
public string FilePath { get; }

protected CertificateBundleX509ChainFactory(X509Certificate2Collection certificates, string filePath = null)
{
Certificates = certificates;
FilePath = filePath;
}

public X509Chain Create()
{
X509Chain x509Chain = new();

x509Chain.ChainPolicy.TrustMode = X509ChainTrustMode.CustomRootTrust;

if (Certificates is not null && Certificates.Count > 0)
{
x509Chain.ChainPolicy.CustomTrustStore.AddRange(Certificates);
}

return x509Chain;
}

protected static bool TryImportFromPemFile(string filePath, out X509Certificate2Collection certificates)
{
certificates = new X509Certificate2Collection();

try
{
certificates.ImportFromPemFile(filePath);

return true;
}
catch (Exception ex) when
(
ex is CryptographicException ||
ex is FileNotFoundException ||
ex is DirectoryNotFoundException
)
{
certificates.Clear();
}

return false;
}
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Security.Cryptography.X509Certificates;

namespace NuGet.Packaging.Signing
{
internal sealed class DotNetDefaultTrustStoreX509ChainFactory : IX509ChainFactory
{
public X509Chain Create()
{
return new X509Chain();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#if NET5_0_OR_GREATER

using System;
using System.IO;
using System.Security.Cryptography.X509Certificates;
using System.Threading;

namespace NuGet.Packaging.Signing
{
internal sealed class FallbackCertificateBundleX509ChainFactory : CertificateBundleX509ChainFactory
{
// These constants are dictated by the .NET SDK.
internal const string SubdirectoryName = "trustedroots";
internal const string FileName = "codesignctl.pem";

private static readonly Lazy<string> ThisAssemblyDirectoryPath = new(GetThisAssemblyDirectoryPath, LazyThreadSafetyMode.ExecutionAndPublication);

private FallbackCertificateBundleX509ChainFactory(X509Certificate2Collection certificates, string filePath)
: base(certificates, filePath)
{
}

internal static bool TryCreate(out FallbackCertificateBundleX509ChainFactory factory, string fileName = FileName)
{
factory = null;

string fullFilePath = Path.Combine(
ThisAssemblyDirectoryPath.Value,
SubdirectoryName,
fileName ?? FileName);

if (TryImportFromPemFile(fullFilePath, out X509Certificate2Collection certificates))
{
factory = new FallbackCertificateBundleX509ChainFactory(certificates, fullFilePath);

return true;
}

return false;
}

private static string GetThisAssemblyDirectoryPath()
{
string location = typeof(FallbackCertificateBundleX509ChainFactory).Assembly.Location;
FileInfo thisAssembly = new(location);

return thisAssembly.DirectoryName;
}
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Security.Cryptography.X509Certificates;

namespace NuGet.Packaging.Signing
{
internal interface IX509ChainFactory
{
X509Chain Create();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#if NET5_0_OR_GREATER

using System.Security.Cryptography.X509Certificates;

namespace NuGet.Packaging.Signing
{
internal sealed class NoCertificateBundleX509ChainFactory : CertificateBundleX509ChainFactory
{
internal NoCertificateBundleX509ChainFactory()
: base(new X509Certificate2Collection())
{
}
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

#if NET5_0_OR_GREATER

using System.Collections.Generic;
using System.Security.Cryptography.X509Certificates;

namespace NuGet.Packaging.Signing
{
internal sealed class SystemCertificateBundleX509ChainFactory : CertificateBundleX509ChainFactory
{
internal static readonly IReadOnlyList<string> ProbePaths = new[]
{
"/etc/pki/ca-trust/extracted/pem/objsign-ca-bundle.pem"
};

private SystemCertificateBundleX509ChainFactory(X509Certificate2Collection certificates, string filePath)
: base(certificates, filePath)
{
}

internal static bool TryCreate(out SystemCertificateBundleX509ChainFactory factory)
{
return TryCreate(ProbePaths, out factory);
}

// For testing purposes only.
internal static bool TryCreate(IReadOnlyList<string> probePaths, out SystemCertificateBundleX509ChainFactory factory)
{
factory = null;

foreach (string probePath in probePaths)
{
if (TryImportFromPemFile(probePath, out X509Certificate2Collection certificates)
&& certificates.Count > 0)
{
factory = new SystemCertificateBundleX509ChainFactory(certificates, probePath);

return true;
}
}

return false;
}
}
}

#endif
Loading