Skip to content

Commit

Permalink
Signing: enable signed package verification on Linux by default in .N…
Browse files Browse the repository at this point in the history
…ET 6 SDK (#4706)

Fix NuGet/Home#11264 and NuGet/Home#11263.
  • Loading branch information
dtivel committed Jul 13, 2022
1 parent 3f9d36b commit 2756cfd
Show file tree
Hide file tree
Showing 57 changed files with 9,096 additions and 286 deletions.
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 @@ -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 = RuntimeEnvironmentHelper.IsLinux;

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
1 change: 1 addition & 0 deletions src/NuGet.Core/NuGet.Packaging/Properties/AssemblyInfo.cs
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

0 comments on commit 2756cfd

Please sign in to comment.