| | 1 | | using System; |
| | 2 | | using System.Collections.Generic; |
| | 3 | | using System.Diagnostics.CodeAnalysis; |
| | 4 | | using System.IO; |
| | 5 | | using System.Runtime.InteropServices; |
| | 6 | | using System.Text; |
| | 7 | |
|
| | 8 | | #pragma warning disable IDE1006 // Naming Styles - off here, because we want to use native names |
| | 9 | |
|
| | 10 | | namespace LockCheck.Linux; |
| | 11 | |
|
| | 12 | | internal static partial class NativeMethods |
| | 13 | | { |
| | 14 | | public const int EAGAIN = 11; // Resource unavailable, try again (same value as EWOULDBLOCK), |
| | 15 | | public const int EACCES = 13; // Mandatory lock |
| | 16 | | public const int EWOULDBLOCK = EAGAIN; // Operation would block. |
| | 17 | | public const int ERANGE = 34; |
| | 18 | |
|
| | 19 | | public static long GetInode(string path) |
| | 20 | | { |
| 2 | 21 | | if (Stat(path, out var status) >= 0) |
| | 22 | | { |
| 2 | 23 | | return status.Ino; |
| | 24 | | } |
| | 25 | |
|
| 2 | 26 | | return -1; |
| | 27 | | } |
| | 28 | |
|
| | 29 | | public static bool TryGetUid(string path, out uint uid) |
| | 30 | | { |
| 2 | 31 | | if (Stat(path, out var status) >= 0) |
| | 32 | | { |
| 2 | 33 | | uid = status.Uid; |
| 2 | 34 | | return true; |
| | 35 | | } |
| | 36 | |
|
| 0 | 37 | | uid = 0; |
| 0 | 38 | | return false; |
| | 39 | | } |
| | 40 | |
|
| | 41 | | public static string? GetUserName(uint uid) |
| | 42 | | { |
| | 43 | | string? userName; |
| | 44 | | unsafe |
| | 45 | | { |
| | 46 | | const int BufLen = Passwd.InitialBufferSize; |
| 2 | 47 | | byte* stackBuf = stackalloc byte[BufLen]; |
| 2 | 48 | | if (TryGetUserName(uid, stackBuf, BufLen, out userName)) |
| | 49 | | { |
| 2 | 50 | | return userName; |
| | 51 | | } |
| | 52 | |
|
| 0 | 53 | | int lastBufLen = BufLen; |
| 0 | 54 | | while (true) |
| | 55 | | { |
| 0 | 56 | | lastBufLen *= 2; |
| 0 | 57 | | byte[] heapBuf = new byte[lastBufLen]; |
| 0 | 58 | | fixed (byte* buf = &heapBuf[0]) |
| | 59 | | { |
| 0 | 60 | | if (TryGetUserName(uid, buf, heapBuf.Length, out userName)) |
| | 61 | | { |
| 0 | 62 | | return userName; |
| | 63 | | } |
| | 64 | | } |
| | 65 | | } |
| | 66 | | } |
| | 67 | | } |
| | 68 | |
|
| | 69 | | private static unsafe bool TryGetUserName(uint uid, byte* buf, int bufLen, out string? userName) |
| | 70 | | { |
| 2 | 71 | | int error = GetPwUidR(uid, out Passwd passwd, buf, bufLen); |
| | 72 | |
|
| | 73 | | // positive error number returned -> failure other than entry-not-found |
| 2 | 74 | | if (error != 0) |
| | 75 | | { |
| 0 | 76 | | userName = null; |
| 0 | 77 | | return false; |
| | 78 | | } |
| | 79 | |
|
| | 80 | | // entry not found |
| 2 | 81 | | if (error == -1) |
| | 82 | | { |
| 0 | 83 | | userName = null; |
| 0 | 84 | | return true; |
| | 85 | | } |
| | 86 | |
|
| 2 | 87 | | userName = Marshal.PtrToStringAnsi((IntPtr)passwd.Name); |
| 2 | 88 | | return true; |
| | 89 | | } |
| | 90 | |
|
| | 91 | | private const string SystemNative = "System.Native"; |
| | 92 | |
|
| | 93 | | [StructLayout(LayoutKind.Sequential)] |
| | 94 | | internal struct FileStatus |
| | 95 | | { |
| | 96 | | internal int Flags; |
| | 97 | | internal int Mode; |
| | 98 | | internal uint Uid; |
| | 99 | | internal uint Gid; |
| | 100 | | internal long Size; |
| | 101 | | internal long ATime; |
| | 102 | | internal long ATimeNsec; |
| | 103 | | internal long MTime; |
| | 104 | | internal long MTimeNsec; |
| | 105 | | internal long CTime; |
| | 106 | | internal long CTimeNsec; |
| | 107 | | internal long BirthTime; |
| | 108 | | internal long BirthTimeNsec; |
| | 109 | | internal long Dev; |
| | 110 | | internal long RDev; |
| | 111 | | internal long Ino; |
| | 112 | | internal uint UserFlags; |
| | 113 | | } |
| | 114 | |
|
| | 115 | | [LibraryImport(SystemNative, EntryPoint = "SystemNative_Stat", StringMarshalling = StringMarshalling.Utf8, SetLastEr |
| | 116 | | private static partial int Stat(string pathname, out FileStatus status); |
| | 117 | |
|
| | 118 | | internal unsafe struct Passwd |
| | 119 | | { |
| | 120 | | internal const int InitialBufferSize = 256; |
| | 121 | |
|
| | 122 | | internal byte* Name; |
| | 123 | | internal byte* Password; |
| | 124 | | internal uint UserId; |
| | 125 | | internal uint GroupId; |
| | 126 | | internal byte* UserInfo; |
| | 127 | | internal byte* HomeDirectory; |
| | 128 | | internal byte* Shell; |
| | 129 | | } |
| | 130 | |
|
| | 131 | | [LibraryImport(SystemNative, EntryPoint = "SystemNative_GetPwUidR", SetLastError = false)] |
| | 132 | | internal static unsafe partial int GetPwUidR(uint uid, out Passwd pwd, byte* buf, int bufLen); |
| | 133 | |
|
| | 134 | | // TODO: Use this stuff. |
| 2 | 135 | | private static readonly HashSet<string> s_critical = |
| 2 | 136 | | [ |
| 2 | 137 | | // List is generated by ChatGPT, obviously ;-) |
| 2 | 138 | | "/lib/systemd/systemd", // systemd init process (modern distros using systemd) |
| 2 | 139 | | "/sbin/init", // init process (legacy or minimal distros not using systemd) |
| 2 | 140 | | "/usr/sbin/cron", // cron scheduler (alternative path) |
| 2 | 141 | | "/usr/sbin/crond", // cron scheduler (for Red Hat-based systems) |
| 2 | 142 | | "/usr/sbin/rsyslogd", // syslog daemon (for system logging) |
| 2 | 143 | | "/usr/sbin/syslogd", // alternative syslog daemon path (legacy or minimal systems) |
| 2 | 144 | | "/usr/sbin/sshd", // OpenSSH daemon for remote access |
| 2 | 145 | | "/usr/lib/NetworkManager/NetworkManager", // network manager (for desktop/server environments) |
| 2 | 146 | | "/sbin/dhcpcd", // DHCP client daemon (lightweight systems) |
| 2 | 147 | | "/usr/sbin/dhclient", // DHCP client daemon (Debian/Ubuntu default) |
| 2 | 148 | | "/usr/lib/polkit-1/polkitd", // policy kit daemon for permissions (common in GUI-based systems) |
| 2 | 149 | | "/usr/lib/upowerd", // power management daemon (laptops/desktops) |
| 2 | 150 | | "/sbin/udevd", // udev device manager daemon (general) |
| 2 | 151 | | "/usr/lib/udev/udevd", // udev device manager daemon (alternative path) |
| 2 | 152 | | "/usr/sbin/ntpd", // NTP daemon for time synchronization (traditional) |
| 2 | 153 | | "/usr/sbin/systemd-timesyncd", // time sync daemon in systemd environments |
| 2 | 154 | | "/usr/sbin/acpid", // ACPI daemon for power events (laptop/desktops) |
| 2 | 155 | | "/usr/sbin/irqbalance", // IRQ balancing daemon (multi-core systems) |
| 2 | 156 | | "/usr/sbin/lvmetad", // LVM metadata daemon (for managing logical volumes) |
| 2 | 157 | | "/sbin/auditd", // audit daemon for tracking security events |
| 2 | 158 | | "/usr/sbin/firewalld", // firewall daemon (often in Red Hat-based systems) |
| 2 | 159 | | "/usr/lib/snapd/snapd", // snap daemon (Ubuntu, for managing snap packages) |
| 2 | 160 | | "/usr/sbin/apparmor", // AppArmor security enforcement daemon (Ubuntu-based) |
| 2 | 161 | | "/usr/sbin/ModemManager", // modem management (often in Linux desktop environments) |
| 2 | 162 | | "/usr/sbin/cupsd", // CUPS printing service daemon |
| 2 | 163 | | "/usr/sbin/bluetoothd", // Bluetooth service daemon (for desktops) |
| 2 | 164 | | "/usr/libexec/fwupd/fwupd", // firmware update daemon (modern desktop systems) |
| 2 | 165 | | "/usr/libexec/udisks2/udisksd", // disk management daemon (common on desktops) |
| 2 | 166 | | "/usr/lib/accounts-daemon/accounts-daemon", // user account management daemon (desktops) |
| 2 | 167 | | "/usr/sbin/clamd", // ClamAV antivirus daemon (for email/AV filtering servers) |
| 2 | 168 | | "/usr/bin/Xorg", // X server daemon (for X11 display server) |
| 2 | 169 | | "/usr/bin/wayland-session", // Wayland session daemon (for Wayland display server) |
| 2 | 170 | | "/usr/bin/gnome-session", // GNOME session manager (often used with Wayland) |
| 2 | 171 | | "/usr/libexec/gnome-settings-daemon", // GNOME settings daemon (for managing user settings) |
| 2 | 172 | | ]; |
| | 173 | |
|
| 0 | 174 | | internal static IEnumerable<string> GetKnownCriticalProcesses() => s_critical; |
| | 175 | |
|
| 2 | 176 | | internal static bool IsProcessCritical(string? executablePath) => executablePath != null && s_critical.Contains(exec |
| | 177 | | } |