247e61d9d8
Previously -e/--exec always ran its command as the host's own invoking
credentials, ignoring whatever uid/gid the session's own -r/--run resolved
it to. Now it defaults to whatever the session's sandboxed command is
already running as (read from /proc/<pid>/status), and --user/--group can
override that, resolved against the session's own /etc/passwd/group
(fetched via nsenter, since the mount namespace isn't otherwise reachable).
Either way it's applied by reusing the already bind-mounted
slocker-lite-priv-drop helper from the original -r/--run, not a second copy.
Refactored resolve_user_and_group() (user_spec.{h,cpp}) to take passwd/group
*content* instead of a filesystem path, so both callers -- run_container()
(local file read) and exec_in_session() (nsenter + cat) -- can share it.
Exported priv_drop::path/helper_name and find_priv_drop_helper() from
bwrap.h so exec_session.cpp can reuse the same helper.
Caught and fixed a second bug during testing on a rootless dev machine:
under -r/--run's own --unshare-user, "root inside the container" is a uid
mapping, not a real privilege drop, so /proc/<pid>/status's uid/gid (read
from outside that namespace) is the host-mapped id, not the container's
own view -- defaulting to a priv-drop there was wrong and failed outright.
Fixed by skipping the default-identity lookup whenever --exec is already
joining a differing user namespace, since nsenter --preserve-credentials
alone already reproduces the container's view via that same kernel mapping.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Gv3s5jckJKzh6JkMoi2Akz
40 lines
2.1 KiB
C++
40 lines
2.1 KiB
C++
// Copyright (C) 2026 Viorel Munteanu
|
|
//
|
|
// This program is free software; you can redistribute it and/or modify
|
|
// it under the terms of the GNU General Public License as published by
|
|
// the Free Software Foundation; either version 2 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this program; if not, write to the Free Software Foundation, Inc.,
|
|
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
|
|
#pragma once
|
|
|
|
#include <optional>
|
|
#include <string>
|
|
|
|
#include "bwrap.h"
|
|
|
|
// Resolves --user (name or numeric uid) and --group (optional, name or numeric gid)
|
|
// against the container's own /etc/passwd and /etc/group *content* -- NOT the
|
|
// host's. Callers are responsible for obtaining that content however is
|
|
// appropriate for them: run_container() (commands.cpp) reads it directly off the
|
|
// merged mount path, while exec_in_session() (exec_session.cpp) fetches it via
|
|
// nsenter, since a running session's mount namespace isn't otherwise reachable
|
|
// from this process. `nullopt` for either means "file unreadable/absent" -- a
|
|
// numeric `user`/`group` still resolves fine without it (see below); a named one
|
|
// doesn't. A numeric `user` with no `group` and no matching /etc/passwd entry
|
|
// defaults gid to the same numeric value as the uid; a named `user` always
|
|
// requires a resolvable /etc/passwd entry (for its uid and default gid). Logs a
|
|
// specific error and returns nullopt if a named user/group can't be resolved.
|
|
std::optional<ResolvedUser> resolve_user_and_group(const std::string& user,
|
|
const std::optional<std::string>& group,
|
|
const std::optional<std::string>& passwd_content,
|
|
const std::optional<std::string>& group_content);
|