2#include "pedigree/kernel/LockGuard.h"
3#include "pedigree/kernel/process/TerminationDeferral.h"
4#include "pedigree/kernel/process/Thread.h"
5#include "pedigree/kernel/processor/Processor.h"
6#include "pedigree/kernel/processor/ProcessorInformation.h"
7#include "pedigree/kernel/syscallError.h"
8#include "pedigree/kernel/utilities/Pointers.h"
9#include "pedigree/kernel/utilities/StaticString.h"
11#include "PosixProcess.h"
12#include "sandbox-state.h"
13#include "user-namespace.h"
16uint64_t nextIdentity = 0xefffffff;
20uint32_t effectiveUid(
Thread& task) {
21 return static_cast<uint32_t
>(task.
getParent()->getEffectiveUserId());
23bool number(
const char*& cursor,
const char* end, uint32_t& value) {
24 while (cursor != end && (*cursor ==
' ' || *cursor ==
'\t')) {
27 if (cursor == end || *cursor <
'0' || *cursor >
'9') {
32 result = result * 10 + (*cursor++ -
'0');
33 if (result > UINT32_MAX) {
36 }
while (cursor != end && *cursor >=
'0' && *cursor <=
'9');
37 value =
static_cast<uint32_t
>(result);
40bool overlaps(uint32_t first, uint32_t count, uint32_t other, uint32_t otherCount) {
41 return uint64_t(first) < uint64_t(other) + otherCount &&
42 uint64_t(other) < uint64_t(first) + count;
45 SYSCALL_ERROR(NotEnoughPermissions);
49 SYSCALL_ERROR(InvalidArgument);
54PosixUserNamespace::PosixUserNamespace(
const UserNamespaceRef& parent, uint32_t owner,
55 uint32_t group,
bool creatorSetfcap)
59 m_Depth(parent ? parent->depth() + 1 : 1),
60 m_Identity(__atomic_add_fetch(&nextIdentity, 1, __ATOMIC_RELAXED)),
61 m_CreatorSetfcap(creatorSetfcap),
62 m_GroupsAllowed(!parent || parent->groupsAllowed()) {}
64bool PosixUserNamespace::toGlobal(
bool group, uint32_t
id, uint32_t& global, uint32_t count)
const {
66 const Map& map = group ? m_Gids : m_Uids;
67 for (
size_t i = 0; i < map.count; ++i) {
68 const auto& range = map.ranges[i];
69 if (
id >= range.inside && uint64_t(
id) + count <= uint64_t(range.inside) + range.count) {
70 global = range.global + (
id - range.inside);
77bool PosixUserNamespace::fromGlobal(
bool group, uint32_t global, uint32_t&
id)
const {
79 const Map& map = group ? m_Gids : m_Uids;
80 for (
size_t i = 0; i < map.count; ++i) {
81 const auto& range = map.ranges[i];
82 if (global >= range.global && uint64_t(global) < uint64_t(range.global) + range.count) {
83 id = range.inside + (global - range.global);
90bool PosixUserNamespace::groupsAllowed()
const {
92 return m_GroupsAllowed;
95int PosixUserNamespace::writeMap(
bool group,
const char* bytes,
size_t length) {
97 if (!length || length >= 4096) {
100 const auto authority = posix_task_credentials(current());
101 if (authority.userNamespace.get() !=
this && authority.userNamespace != m_Parent) {
104 const unsigned capability = group ? PosixCapabilities::Setgid : PosixCapabilities::Setuid;
106 if (authority.userNamespace.get() ==
this) {
107 if (!(authority.effective & (uint64_t(1) << capability))) {
110 }
else if (effectiveUid(current()) != m_Owner && !posix_namespace_capable(m_Parent, capability)) {
115 Map* parsed = storage.get();
117 SYSCALL_ERROR(OutOfMemory);
120 const char* cursor = bytes;
121 const char* end = bytes + length;
122 while (cursor != end) {
123 if (parsed->count == MaximumRanges) {
126 auto& range = parsed->ranges[parsed->count];
127 if (!number(cursor, end, range.inside) || cursor == end ||
128 (*cursor !=
' ' && *cursor !=
'\t') || !number(cursor, end, range.outside) ||
129 cursor == end || (*cursor !=
' ' && *cursor !=
'\t') || !number(cursor, end, range.count) ||
130 !range.count || uint64_t(range.inside) + range.count > UINT32_MAX ||
131 uint64_t(range.outside) + range.count > UINT32_MAX) {
134 while (cursor != end && (*cursor ==
' ' || *cursor ==
'\t')) {
137 if (cursor != end && *cursor++ !=
'\n') {
140 for (
size_t i = 0; i < parsed->count; ++i) {
141 const auto& previous = parsed->ranges[i];
142 if (overlaps(range.inside, range.count, previous.inside, previous.count) ||
143 overlaps(range.outside, range.count, previous.outside, previous.count)) {
147 range.global = range.outside;
148 if (m_Parent && !m_Parent->toGlobal(group, range.outside, range.global, range.count)) {
151 if (!group && range.outside == 0 &&
152 !(authority.userNamespace.get() ==
this
154 : posix_namespace_capable(m_Parent, PosixCapabilities::Setfcap))) {
159 const bool parentPrivilege = posix_namespace_capable(m_Parent, capability);
160 const uint32_t real =
161 group ? current().getParent()->getGroupId() : current().getParent()->getUserId();
163 Map& destination = group ? m_Gids : m_Uids;
164 if (destination.count) {
167 if (!parentPrivilege &&
168 (parsed->count != 1 || parsed->ranges[0].count != 1 || parsed->ranges[0].global != real ||
169 effectiveUid(current()) != m_Owner || (group && m_GroupsAllowed))) {
172 destination = *parsed;
173 current().setErrno(0);
174 return static_cast<int>(length);
177size_t PosixUserNamespace::readMap(
bool group,
const UserNamespaceRef& viewer,
char* bytes,
178 size_t capacity)
const {
180 Map* snapshot = storage.get();
182 SYSCALL_ERROR(OutOfMemory);
187 *snapshot = group ? m_Gids : m_Uids;
190 for (
size_t i = 0; i < snapshot->count; ++i) {
191 const auto& range = snapshot->ranges[i];
192 uint32_t outside = range.global;
193 if (viewer.
get() ==
this) {
194 outside = range.outside;
195 }
else if (viewer && !viewer->fromGlobal(group, range.global, outside)) {
199 line.append(range.inside);
201 line.append(outside);
203 line.append(range.count);
205 const size_t copied = line.length() < capacity - used ? line.length() : capacity - used;
206 MemoryCopy(bytes + used,
static_cast<const char*
>(line), copied);
208 if (used == capacity) {
215int PosixUserNamespace::writeSetgroups(
const char* bytes,
size_t length) {
218 if ((length == 4 || (length == 5 && bytes[4] ==
'\n')) && !MemoryCompare(bytes,
"deny", 4)) {
220 }
else if ((length == 5 || (length == 6 && bytes[5] ==
'\n')) &&
221 !MemoryCompare(bytes,
"allow", 5)) {
226 const auto authority = posix_task_credentials(current());
227 if (authority.userNamespace.get() !=
this && authority.userNamespace != m_Parent) {
230 if (authority.userNamespace.get() ==
this) {
231 if (!(authority.effective & (uint64_t(1) << PosixCapabilities::SysAdmin))) {
234 }
else if (effectiveUid(current()) != m_Owner &&
235 !posix_namespace_capable(m_Parent, PosixCapabilities::SysAdmin)) {
239 if (m_Gids.count || (allow && !m_GroupsAllowed)) {
242 m_GroupsAllowed = allow;
243 current().setErrno(0);
244 return static_cast<int>(length);
248 auto stored = posix_sandbox_credentials(task);
253 if (!effectiveUid(task)) {
254 result.permitted = result.effective = PosixCapabilities::All;
259 auto stored = posix_sandbox_credentials(task);
263 if (capability > PosixCapabilities::Last) {
266 const auto authority = posix_task_credentials(task);
269 if (space == authority.userNamespace) {
270 return authority.effective & (uint64_t(1) << capability);
275 if (space->parent() == authority.userNamespace && space->owner() == effectiveUid(task)) {
278 space = space->parent();
281bool posix_namespace_capable(
const UserNamespaceRef& space,
unsigned capability) {
282 return posix_namespace_capable(current(), space, capability);
284bool posix_capable(
unsigned capability) {
285 return posix_namespace_capable(posix_user_namespace(current()), capability);
287bool posix_global_capable(
unsigned capability) {
291 auto parent = posix_user_namespace(
creator);
292 if (parent && parent->depth() >= 32) {
293 SYSCALL_ERROR(NoSpaceLeftOnDevice);
296 uint32_t uid = effectiveUid(
creator);
297 uint32_t gid =
creator.getParent()->getEffectiveGroupId();
300 (!parent->fromGlobal(
false, uid, ignored) || !parent->fromGlobal(
true, gid, ignored))) {
301 SYSCALL_ERROR(NotEnoughPermissions);
305 parent, uid, gid, posix_namespace_capable(
creator, parent, PosixCapabilities::Setfcap));
307 if (!space || !replacement) {
308 SYSCALL_ERROR(OutOfMemory);
311 replacement->userNamespace = space;
312 replacement->effective = replacement->permitted = PosixCapabilities::All;
313 result = replacement;
316uint32_t posix_visible_id(
bool group, uint32_t global) {
317 auto space = posix_user_namespace(current());
318 uint32_t visible = global;
319 if (space && !space->fromGlobal(group, global, visible)) {
324bool posix_global_id(
bool group, uint32_t visible, uint32_t& global) {
325 if (visible == UINT32_MAX) {
329 auto space = posix_user_namespace(current());
331 return !space || space->toGlobal(group, visible, global);
static ProcessorInformation & information()
static SharedPointer< PosixUserNamespace > tryAllocate(Args...)
Process * getParent() const