Honor permissions in OpenFileSystemWithId. Add basic AC test

This commit is contained in:
Alex Barney 2020-08-23 23:35:00 -07:00
parent 3184e6ca7e
commit 6496a2c1bc
13 changed files with 304 additions and 11 deletions

View file

@ -1,4 +1,5 @@
Name,Index Name,Index
Svc,1
Fs,2 Fs,2
Loader,9 Loader,9
Sf,10 Sf,10

1 Name Index
2 Svc 1
3 Fs 2
4 Loader 9
5 Sf 10

View file

@ -1,4 +1,5 @@
Name,Namespace,Path Name,Namespace,Path
Svc,LibHac.Svc,LibHac/Svc/ResultSvc.cs
Fs,LibHac.Fs,LibHac/Fs/ResultFs.cs Fs,LibHac.Fs,LibHac/Fs/ResultFs.cs
Loader,LibHac.Loader,LibHac/Loader/ResultLoader.cs Loader,LibHac.Loader,LibHac/Loader/ResultLoader.cs
Sf,LibHac.Sf,LibHac/Sf/ResultSf.cs Sf,LibHac.Sf,LibHac/Sf/ResultSf.cs

1 Name Namespace Path
2 Svc LibHac.Svc LibHac/Svc/ResultSvc.cs
3 Fs LibHac.Fs LibHac/Fs/ResultFs.cs
4 Loader LibHac.Loader LibHac/Loader/ResultLoader.cs
5 Sf LibHac.Sf LibHac/Sf/ResultSf.cs

View file

@ -1,4 +1,53 @@
Module,DescriptionStart,DescriptionEnd,Name,Summary Module,DescriptionStart,DescriptionEnd,Name,Summary
1,7,,OutOfSessions,
1,14,,InvalidArgument,
1,33,,NotImplemented,
1,54,,StopProcessingException,
1,57,,NoSynchronizationObject,
1,59,,TerminationRequested,
1,70,,NoEvent,
1,101,,InvalidSize,
1,102,,InvalidAddress,
1,103,,OutOfResource,
1,104,,OutOfMemory,
1,105,,OutOfHandles,
1,106,,InvalidCurrentMemory,
1,108,,InvalidNewMemoryPermission,
1,110,,InvalidMemoryRegion,
1,112,,InvalidPriority,
1,113,,InvalidCoreId,
1,114,,InvalidHandle,
1,115,,InvalidPointer,
1,116,,InvalidCombination,
1,117,,TimedOut,
1,118,,Cancelled,
1,119,,OutOfRange,
1,120,,InvalidEnumValue,
1,121,,NotFound,
1,122,,Busy,
1,123,,SessionClosed,
1,124,,NotHandled,
1,125,,InvalidState,
1,126,,ReservedUsed,
1,127,,NotSupported,
1,128,,Debug,
1,129,,NoThread,
1,130,,UnknownThread,
1,131,,PortClosed,
1,132,,LimitReached,
1,133,,InvalidMemoryPool,
1,258,,ReceiveListBroken,
1,259,,OutOfAddressSpace,
1,260,,MessageTooLarge,
1,517,,InvalidProcessId,
1,518,,InvalidThreadId,
1,519,,InvalidId,
1,520,,ProcessTerminated,
2,0,999,HandledByAllProcess, 2,0,999,HandledByAllProcess,
2,1,,PathNotFound,Specified path does not exist 2,1,,PathNotFound,Specified path does not exist
2,2,,PathAlreadyExists,Specified path already exists 2,2,,PathAlreadyExists,Specified path already exists

1 Module DescriptionStart DescriptionEnd Name Summary
2 1 7 OutOfSessions
3 1 14 InvalidArgument
4 1 33 NotImplemented
5 1 54 StopProcessingException
6 1 57 NoSynchronizationObject
7 1 59 TerminationRequested
8 1 70 NoEvent
9 1 101 InvalidSize
10 1 102 InvalidAddress
11 1 103 OutOfResource
12 1 104 OutOfMemory
13 1 105 OutOfHandles
14 1 106 InvalidCurrentMemory
15 1 108 InvalidNewMemoryPermission
16 1 110 InvalidMemoryRegion
17 1 112 InvalidPriority
18 1 113 InvalidCoreId
19 1 114 InvalidHandle
20 1 115 InvalidPointer
21 1 116 InvalidCombination
22 1 117 TimedOut
23 1 118 Cancelled
24 1 119 OutOfRange
25 1 120 InvalidEnumValue
26 1 121 NotFound
27 1 122 Busy
28 1 123 SessionClosed
29 1 124 NotHandled
30 1 125 InvalidState
31 1 126 ReservedUsed
32 1 127 NotSupported
33 1 128 Debug
34 1 129 NoThread
35 1 130 UnknownThread
36 1 131 PortClosed
37 1 132 LimitReached
38 1 133 InvalidMemoryPool
39 1 258 ReceiveListBroken
40 1 259 OutOfAddressSpace
41 1 260 MessageTooLarge
42 1 517 InvalidProcessId
43 1 518 InvalidThreadId
44 1 519 InvalidId
45 1 520 ProcessTerminated
46 2 0 999 HandledByAllProcess
47 2 1 PathNotFound Specified path does not exist
48 2 2 PathAlreadyExists Specified path already exists
49 2 7 TargetLocked Resource already in use (file already opened, savedata filesystem already mounted)
50 2 8 DirectoryNotEmpty Specified directory is not empty when trying to delete it
51 2 0 13 999 HandledByAllProcess DirectoryStatusChanged
52 2 1 30 45 PathNotFound InsufficientFreeSpace Specified path does not exist
53 2 2 31 PathAlreadyExists UsableSpaceNotEnoughForSaveData Specified path already exists

View file

@ -64,7 +64,7 @@ namespace LibHac.Fs
if (rc.IsFailure()) if (rc.IsFailure())
{ {
throw new HorizonResultException(rc, "Failed to create file system proxy service object."); throw new HorizonResultException(rc, "Failed to get file system proxy service object.");
} }
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult(); fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
@ -91,7 +91,7 @@ namespace LibHac.Fs
if (rc.IsFailure()) if (rc.IsFailure())
{ {
throw new HorizonResultException(rc, "Failed to create file system proxy service object."); throw new HorizonResultException(rc, "Failed to get file system proxy service object.");
} }
fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult(); fsProxy.SetCurrentProcess(Hos.Os.GetCurrentProcessId().Value).IgnoreResult();
@ -118,7 +118,7 @@ namespace LibHac.Fs
if (rc.IsFailure()) if (rc.IsFailure())
{ {
throw new HorizonResultException(rc, "Failed to create registry service object."); throw new HorizonResultException(rc, "Failed to get registry service object.");
} }
ProgramRegistry = registry; ProgramRegistry = registry;

View file

@ -45,15 +45,58 @@ namespace LibHac.FsSrv
{ {
fileSystem = default; fileSystem = default;
// Missing permission check, speed emulation storage type wrapper, and FileSystemInterfaceAdapter Result rc = GetProgramInfo(out ProgramInfo programInfo);
if (rc.IsFailure()) return rc;
bool canMountSystemDataPrivate = false; AccessControl ac = programInfo.AccessControl;
switch (type)
{
case FileSystemProxyType.Logo:
if (!ac.GetAccessibilityFor(AccessibilityType.MountLogo).CanRead)
return ResultFs.PermissionDenied.Log();
break;
case FileSystemProxyType.Control:
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentControl).CanRead)
return ResultFs.PermissionDenied.Log();
break;
case FileSystemProxyType.Manual:
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentManual).CanRead)
return ResultFs.PermissionDenied.Log();
break;
case FileSystemProxyType.Meta:
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentMeta).CanRead)
return ResultFs.PermissionDenied.Log();
break;
case FileSystemProxyType.Data:
if (!ac.GetAccessibilityFor(AccessibilityType.MountContentData).CanRead)
return ResultFs.PermissionDenied.Log();
break;
case FileSystemProxyType.Package:
if (!ac.GetAccessibilityFor(AccessibilityType.MountApplicationPackage).CanRead)
return ResultFs.PermissionDenied.Log();
break;
default:
return ResultFs.InvalidArgument.Log();
}
if (type == FileSystemProxyType.Meta)
{
id = ulong.MaxValue;
}
else if (id == ulong.MaxValue)
{
return ResultFs.InvalidArgument.Log();
}
bool canMountSystemDataPrivate = ac.GetAccessibilityFor(AccessibilityType.MountSystemDataPrivate).CanRead;
var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path)); var normalizer = new PathNormalizer(path, GetPathNormalizerOptions(path));
if (normalizer.Result.IsFailure()) return normalizer.Result; if (normalizer.Result.IsFailure()) return normalizer.Result;
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
return FsProxyCore.OpenFileSystem(out fileSystem, normalizer.Path, type, canMountSystemDataPrivate, id); return FsProxyCore.OpenFileSystem(out fileSystem, normalizer.Path, type, canMountSystemDataPrivate, id);
// Missing speed emulation storage type wrapper, async wrapper, and FileSystemInterfaceAdapter
} }
private PathNormalizer.Option GetPathNormalizerOptions(U8Span path) private PathNormalizer.Option GetPathNormalizerOptions(U8Span path)

View file

@ -180,7 +180,7 @@ namespace LibHac.FsSrv.Impl
const int initialProcessIdLowerBound = 1; const int initialProcessIdLowerBound = 1;
const int initialProcessIdUpperBound = 0x50; const int initialProcessIdUpperBound = 0x50;
return initialProcessIdLowerBound >= processId && processId <= initialProcessIdUpperBound; return initialProcessIdLowerBound <= processId && processId <= initialProcessIdUpperBound;
} }
internal static ProgramInfo CreateProgramInfoForInitialProcess(FileSystemServer fsServer) internal static ProgramInfo CreateProgramInfoForInitialProcess(FileSystemServer fsServer)

View file

@ -29,7 +29,7 @@ namespace LibHac.FsSrv.Sf
Span<byte> str = SpanHelpers.AsByteSpan(ref fspPath); Span<byte> str = SpanHelpers.AsByteSpan(ref fspPath);
// Ensure null terminator even if the creation fails for safety // Ensure null terminator even if the creation fails for safety
str[0x301] = 0; str[MaxLength] = 0;
var sb = new U8StringBuilder(str); var sb = new U8StringBuilder(str);
bool overflowed = sb.Append(path).Overflowed; bool overflowed = sb.Append(path).Overflowed;

View file

@ -27,7 +27,7 @@ namespace LibHac.FsSystem
Unsafe.SkipInit(out fsPath); Unsafe.SkipInit(out fsPath);
// Ensure null terminator even if the creation fails for safety // Ensure null terminator even if the creation fails for safety
fsPath.Str[0x301] = 0; fsPath.Str[MaxLength] = 0;
var sb = new U8StringBuilder(fsPath.Str); var sb = new U8StringBuilder(fsPath.Str);
bool overflowed = sb.Append(path).Overflowed; bool overflowed = sb.Append(path).Overflowed;

View file

@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("LibHac.Tests")]

View file

@ -2,7 +2,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using LibHac.Common; using LibHac.Common;
using LibHac.Sf; using LibHac.Svc;
namespace LibHac.Sm namespace LibHac.Sm
{ {
@ -22,7 +22,7 @@ namespace LibHac.Sm
if (!Services.TryGetValue(serviceName, out IServiceObject service)) if (!Services.TryGetValue(serviceName, out IServiceObject service))
{ {
return ResultSf.RequestDeferredByUser.Log(); return ResultSvc.NotFound.Log();
} }
return service.GetServiceObject(out serviceObject); return service.GetServiceObject(out serviceObject);

107
src/LibHac/Svc/ResultSvc.cs Normal file
View file

@ -0,0 +1,107 @@
//-----------------------------------------------------------------------------
// This file was automatically generated.
// Changes to this file will be lost when the file is regenerated.
//
// To change this file, modify /build/CodeGen/results.csv at the root of this
// repo and run the build script.
//
// The script can be run with the "codegen" option to run only the
// code generation portion of the build.
//-----------------------------------------------------------------------------
namespace LibHac.Svc
{
public static class ResultSvc
{
public const int ModuleSvc = 1;
/// <summary>Error code: 2001-0007; Inner value: 0xe01</summary>
public static Result.Base OutOfSessions => new Result.Base(ModuleSvc, 7);
/// <summary>Error code: 2001-0014; Inner value: 0x1c01</summary>
public static Result.Base InvalidArgument => new Result.Base(ModuleSvc, 14);
/// <summary>Error code: 2001-0033; Inner value: 0x4201</summary>
public static Result.Base NotImplemented => new Result.Base(ModuleSvc, 33);
/// <summary>Error code: 2001-0054; Inner value: 0x6c01</summary>
public static Result.Base StopProcessingException => new Result.Base(ModuleSvc, 54);
/// <summary>Error code: 2001-0057; Inner value: 0x7201</summary>
public static Result.Base NoSynchronizationObject => new Result.Base(ModuleSvc, 57);
/// <summary>Error code: 2001-0059; Inner value: 0x7601</summary>
public static Result.Base TerminationRequested => new Result.Base(ModuleSvc, 59);
/// <summary>Error code: 2001-0070; Inner value: 0x8c01</summary>
public static Result.Base NoEvent => new Result.Base(ModuleSvc, 70);
/// <summary>Error code: 2001-0101; Inner value: 0xca01</summary>
public static Result.Base InvalidSize => new Result.Base(ModuleSvc, 101);
/// <summary>Error code: 2001-0102; Inner value: 0xcc01</summary>
public static Result.Base InvalidAddress => new Result.Base(ModuleSvc, 102);
/// <summary>Error code: 2001-0103; Inner value: 0xce01</summary>
public static Result.Base OutOfResource => new Result.Base(ModuleSvc, 103);
/// <summary>Error code: 2001-0104; Inner value: 0xd001</summary>
public static Result.Base OutOfMemory => new Result.Base(ModuleSvc, 104);
/// <summary>Error code: 2001-0105; Inner value: 0xd201</summary>
public static Result.Base OutOfHandles => new Result.Base(ModuleSvc, 105);
/// <summary>Error code: 2001-0106; Inner value: 0xd401</summary>
public static Result.Base InvalidCurrentMemory => new Result.Base(ModuleSvc, 106);
/// <summary>Error code: 2001-0108; Inner value: 0xd801</summary>
public static Result.Base InvalidNewMemoryPermission => new Result.Base(ModuleSvc, 108);
/// <summary>Error code: 2001-0110; Inner value: 0xdc01</summary>
public static Result.Base InvalidMemoryRegion => new Result.Base(ModuleSvc, 110);
/// <summary>Error code: 2001-0112; Inner value: 0xe001</summary>
public static Result.Base InvalidPriority => new Result.Base(ModuleSvc, 112);
/// <summary>Error code: 2001-0113; Inner value: 0xe201</summary>
public static Result.Base InvalidCoreId => new Result.Base(ModuleSvc, 113);
/// <summary>Error code: 2001-0114; Inner value: 0xe401</summary>
public static Result.Base InvalidHandle => new Result.Base(ModuleSvc, 114);
/// <summary>Error code: 2001-0115; Inner value: 0xe601</summary>
public static Result.Base InvalidPointer => new Result.Base(ModuleSvc, 115);
/// <summary>Error code: 2001-0116; Inner value: 0xe801</summary>
public static Result.Base InvalidCombination => new Result.Base(ModuleSvc, 116);
/// <summary>Error code: 2001-0117; Inner value: 0xea01</summary>
public static Result.Base TimedOut => new Result.Base(ModuleSvc, 117);
/// <summary>Error code: 2001-0118; Inner value: 0xec01</summary>
public static Result.Base Cancelled => new Result.Base(ModuleSvc, 118);
/// <summary>Error code: 2001-0119; Inner value: 0xee01</summary>
public static Result.Base OutOfRange => new Result.Base(ModuleSvc, 119);
/// <summary>Error code: 2001-0120; Inner value: 0xf001</summary>
public static Result.Base InvalidEnumValue => new Result.Base(ModuleSvc, 120);
/// <summary>Error code: 2001-0121; Inner value: 0xf201</summary>
public static Result.Base NotFound => new Result.Base(ModuleSvc, 121);
/// <summary>Error code: 2001-0122; Inner value: 0xf401</summary>
public static Result.Base Busy => new Result.Base(ModuleSvc, 122);
/// <summary>Error code: 2001-0123; Inner value: 0xf601</summary>
public static Result.Base SessionClosed => new Result.Base(ModuleSvc, 123);
/// <summary>Error code: 2001-0124; Inner value: 0xf801</summary>
public static Result.Base NotHandled => new Result.Base(ModuleSvc, 124);
/// <summary>Error code: 2001-0125; Inner value: 0xfa01</summary>
public static Result.Base InvalidState => new Result.Base(ModuleSvc, 125);
/// <summary>Error code: 2001-0126; Inner value: 0xfc01</summary>
public static Result.Base ReservedUsed => new Result.Base(ModuleSvc, 126);
/// <summary>Error code: 2001-0127; Inner value: 0xfe01</summary>
public static Result.Base NotSupported => new Result.Base(ModuleSvc, 127);
/// <summary>Error code: 2001-0128; Inner value: 0x10001</summary>
public static Result.Base Debug => new Result.Base(ModuleSvc, 128);
/// <summary>Error code: 2001-0129; Inner value: 0x10201</summary>
public static Result.Base NoThread => new Result.Base(ModuleSvc, 129);
/// <summary>Error code: 2001-0130; Inner value: 0x10401</summary>
public static Result.Base UnknownThread => new Result.Base(ModuleSvc, 130);
/// <summary>Error code: 2001-0131; Inner value: 0x10601</summary>
public static Result.Base PortClosed => new Result.Base(ModuleSvc, 131);
/// <summary>Error code: 2001-0132; Inner value: 0x10801</summary>
public static Result.Base LimitReached => new Result.Base(ModuleSvc, 132);
/// <summary>Error code: 2001-0133; Inner value: 0x10a01</summary>
public static Result.Base InvalidMemoryPool => new Result.Base(ModuleSvc, 133);
/// <summary>Error code: 2001-0258; Inner value: 0x20401</summary>
public static Result.Base ReceiveListBroken => new Result.Base(ModuleSvc, 258);
/// <summary>Error code: 2001-0259; Inner value: 0x20601</summary>
public static Result.Base OutOfAddressSpace => new Result.Base(ModuleSvc, 259);
/// <summary>Error code: 2001-0260; Inner value: 0x20801</summary>
public static Result.Base MessageTooLarge => new Result.Base(ModuleSvc, 260);
/// <summary>Error code: 2001-0517; Inner value: 0x40a01</summary>
public static Result.Base InvalidProcessId => new Result.Base(ModuleSvc, 517);
/// <summary>Error code: 2001-0518; Inner value: 0x40c01</summary>
public static Result.Base InvalidThreadId => new Result.Base(ModuleSvc, 518);
/// <summary>Error code: 2001-0519; Inner value: 0x40e01</summary>
public static Result.Base InvalidId => new Result.Base(ModuleSvc, 519);
/// <summary>Error code: 2001-0520; Inner value: 0x41001</summary>
public static Result.Base ProcessTerminated => new Result.Base(ModuleSvc, 520);
}
}

View file

@ -0,0 +1,64 @@
using LibHac.Common;
using LibHac.Fs;
using LibHac.Fs.Shim;
using LibHac.FsSrv.Impl;
using LibHac.Ncm;
using Xunit;
using ContentType = LibHac.Fs.ContentType;
namespace LibHac.Tests.FsSrv
{
public class AccessControlTests
{
[Fact]
public void OpenFileSystemWithNoPermissions_ReturnsPermissionDenied()
{
Horizon hos = HorizonFactory.CreateBasicHorizon();
HorizonClient regClient = hos.CreatePrivilegedHorizonClient();
HorizonClient client = hos.CreateHorizonClient();
var dataHeader = new AccessControlDataHeader();
var descriptor = new AccessControlDescriptor();
descriptor.Version = 123;
dataHeader.Version = 123;
descriptor.AccessFlags = (ulong)AccessControlBits.Bits.None;
dataHeader.AccessFlags = (ulong)AccessControlBits.Bits.None;
Assert.Success(regClient.Fs.RegisterProgram(client.ProcessId.Value, new ProgramId(123),
StorageId.BuiltInUser, SpanHelpers.AsReadOnlyByteSpan(in dataHeader),
SpanHelpers.AsReadOnlyByteSpan(in descriptor)));
Result rc = client.Fs.MountContent("test".ToU8Span(), "@System:/fake.nca".ToU8Span(), ContentType.Control);
Assert.Result(ResultFs.PermissionDenied, rc);
}
[Fact]
public void OpenFileSystemWithPermissions_ReturnsInvalidNcaMountPoint()
{
Horizon hos = HorizonFactory.CreateBasicHorizon();
HorizonClient regClient = hos.CreatePrivilegedHorizonClient();
HorizonClient client = hos.CreateHorizonClient();
var dataHeader = new AccessControlDataHeader();
var descriptor = new AccessControlDescriptor();
descriptor.Version = 123;
dataHeader.Version = 123;
descriptor.AccessFlags = (ulong)AccessControlBits.Bits.ApplicationInfo;
dataHeader.AccessFlags = (ulong)AccessControlBits.Bits.ApplicationInfo;
Assert.Success(regClient.Fs.RegisterProgram(client.ProcessId.Value, new ProgramId(123),
StorageId.BuiltInUser, SpanHelpers.AsReadOnlyByteSpan(in dataHeader),
SpanHelpers.AsReadOnlyByteSpan(in descriptor)));
// We should get InvalidNcaMountPoint because mounting NCAs from @System isn't allowed
Result rc = client.Fs.MountContent("test".ToU8Span(), "@System:/fake.nca".ToU8Span(), ContentType.Control);
Assert.Result(ResultFs.InvalidNcaMountPoint, rc);
}
}
}

View file

@ -0,0 +1,25 @@
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSrv;
namespace LibHac.Tests
{
public static class HorizonFactory
{
public static Horizon CreateBasicHorizon()
{
IFileSystem rootFs = new InMemoryFileSystem();
var defaultObjects = DefaultFsServerObjects.GetDefaultEmulatedCreators(rootFs, new Keyset());
var config = new FileSystemServerConfig();
config.FsCreators = defaultObjects.FsCreators;
config.DeviceOperator = defaultObjects.DeviceOperator;
config.ExternalKeySet = new ExternalKeySet();
var horizon = new Horizon(new StopWatchTimeSpanGenerator(), config);
return horizon;
}
}
}