/
DefaultLinuxSecureArrayCall.cs
112 lines (100 loc) · 4.08 KB
/
DefaultLinuxSecureArrayCall.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
// <copyright file="DefaultLinuxSecureArrayCall.cs" company="Isopoh">
// To the extent possible under law, the author(s) have dedicated all copyright
// and related and neighboring rights to this software to the public domain
// worldwide. This software is distributed without any warranty.
// </copyright>
namespace Isopoh.Cryptography.SecureArray
{
using System;
using System.Runtime.InteropServices;
using Isopoh.Cryptography.SecureArray.LinuxNative;
/// <summary>
/// A <see cref="SecureArrayCall"/> with defaults for the Linux operating system.
/// </summary>
public class DefaultLinuxSecureArrayCall : SecureArrayCall
{
/// <summary>
/// Initializes a new instance of the <see cref="DefaultLinuxSecureArrayCall"/> class.
/// </summary>
public DefaultLinuxSecureArrayCall()
: base(
(m, l) => UnsafeNativeMethods.LinuxMemset(m, 0, l),
LinuxLockMemory,
(m, l) =>
{
_ = UnsafeNativeMethods.LinuxMunlock(m, l);
},
"Linux")
{
}
#nullable enable
private static string? LinuxLockMemory(IntPtr m, UIntPtr l)
#nullable restore
{
if (UnsafeNativeMethods.LinuxMlock(m, l) != 0)
{
var errorCode = Marshal.GetLastWin32Error();
#nullable enable
if (LinuxTryRaiseCurrentMlockLimit(out string? raiseError))
#nullable restore
{
if (UnsafeNativeMethods.LinuxMlock(m, l) == 0)
{
return null;
}
errorCode = Marshal.GetLastWin32Error();
}
return $"mlock error: {LinuxStrError(errorCode)}{(raiseError == null ? string.Empty : $" ({raiseError})")}";
}
return null;
}
#nullable enable
private static bool LinuxTryRaiseCurrentMlockLimit(out string? error)
#nullable restore
{
var rlimit = new UnsafeNativeMethods.LinuxRlimit { RlimCur = 0, RlimMax = 0 }; // not sure always 64-bit RlimCur and RLimMax values
int rlimitMemlock = 8; // not sure RLIMIT_MEMLOCK is always 8
bool ret = false;
if (UnsafeNativeMethods.LinuxGetRLimit(rlimitMemlock, ref rlimit) != 0)
{
error = $"attempted getrlimit(RLIMIT_MEMLOCK), got error: {LinuxStrError(Marshal.GetLastWin32Error())}.";
return false;
}
if (rlimit.RlimCur < rlimit.RlimMax)
{
rlimit.RlimCur = rlimit.RlimMax;
if (UnsafeNativeMethods.LinuxSetRLimit(rlimitMemlock, ref rlimit) != 0)
{
error = $"attempted setrlimit(RLIMIT_MEMLOCK, {{{rlimit.RlimCur}, {rlimit.RlimMax}}}), got error: {LinuxStrError(Marshal.GetLastWin32Error())}.";
return false;
}
ret = true;
}
// Go for broke
var currentMax = rlimit.RlimMax;
rlimit.RlimCur = ulong.MaxValue;
rlimit.RlimMax = ulong.MaxValue;
if (UnsafeNativeMethods.LinuxSetRLimit(rlimitMemlock, ref rlimit) == 0)
{
error = null;
return true;
}
error = $"attempted setrlimit(RLIMIT_MEMLOCK, {{{rlimit.RlimCur}, {rlimit.RlimMax}}}) on current max {currentMax} bytes, got error: {LinuxStrError(Marshal.GetLastWin32Error())}.";
return ret;
}
private static string LinuxStrError(int errno)
{
var buf = new byte[256];
var bufHandle = GCHandle.Alloc(buf, GCHandleType.Pinned);
try
{
IntPtr bufPtr = UnsafeNativeMethods.LinuxSterrorR(errno, bufHandle.AddrOfPinnedObject(), (ulong)buf.Length);
return Marshal.PtrToStringAnsi(bufPtr) ?? $"Unknown error {errno}";
}
finally
{
bufHandle.Free();
}
}
}
}