Commit e9fc593c authored by Cyrill Gorcunov's avatar Cyrill Gorcunov Committed by Pavel Emelyanov

creds: dump -- Implement per-thread dump of credentials

This as well as restore requires several steps to reach per-thread
support during dump stage

 - @creds area to be fetched from the parasite is embedded into
   parasite_dump_structure

 - when test for task to be dumpable we no longer compare caps
   because we now allow them to be different (and I renamed
   proc_status_creds_eq to proc_status_creds_dumpable for this
   sake)

 - have to extend dump_thread_common to support dumping of
   creds (we call for dump_thread_common in several places,
   in particular when we need to fetch misc params we don't
   need creds, here @creds option comes into the play)

 - after this patch no creds-X.img file be generated anymore,
   I guess we might drop it off with time from descriptors

https://jira.sw.ru/browse/PSBM-41416

v2:
 - In dump_task_creds() don't mangle the call for parasite_dump_creds
   and collect_lsm_profile
 - PARASITE_MAX_GROUPS takes parasite_dump_thread into account because
   dump_thread_common now serves two cases: for plain misc parameters
   fetching and for creds as well (depending on the context)
 - when test for dumpable we still require the seccomp filters
   to match, they can be different and we need to support such
   configuration too but not in this series

v3:
 - Rip off dump_task_creds completely, together with PARASITE_CMD_DUMP_CREDS,
   we dump creds unconditionally in dump_thread_common
 - the group leader thread data is fetched via new
   parasite_dump_thread_leader_seized helper
Signed-off-by: 's avatarCyrill Gorcunov <gorcunov@openvz.org>
Acked-by: 's avatarTycho Andersen <tycho.andersen@canonical.com>
Signed-off-by: 's avatarPavel Emelyanov <xemul@parallels.com>
parent eb5d8442
......@@ -511,24 +511,6 @@ err:
return ret;
}
static int dump_task_creds(struct parasite_ctl *ctl,
const struct cr_imgset *fds)
{
CredsEntry ce = CREDS_ENTRY__INIT;
pr_info("\n");
pr_info("Dumping creds for %d)\n", ctl->pid.real);
pr_info("----------------------------------------\n");
if (parasite_dump_creds(ctl, &ce) < 0)
return -1;
if (collect_lsm_profile(ctl->pid.real, &ce) < 0)
return -1;
return pb_write_one(img_from_set(fds, CR_FD_CREDS), &ce, PB_CREDS);
}
static int get_task_futex_robust_list(pid_t pid, ThreadCoreEntry *info)
{
struct robust_list_head *head = NULL;
......@@ -674,7 +656,9 @@ int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread
int ret;
ThreadCoreEntry *tc = core->thread_core;
ret = get_task_futex_robust_list(pid, tc);
ret = collect_lsm_profile(pid, tc->creds);
if (!ret)
ret = get_task_futex_robust_list(pid, tc);
if (!ret)
ret = dump_sched_info(pid, tc);
if (!ret) {
......@@ -691,10 +675,10 @@ int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread
return ret;
}
static int dump_task_core_all(struct pstree_item *item,
const struct proc_pid_stat *stat,
const struct parasite_dump_misc *misc,
const struct cr_imgset *cr_imgset)
static int dump_task_core_all(struct parasite_ctl *ctl,
struct pstree_item *item,
const struct proc_pid_stat *stat,
const struct cr_imgset *cr_imgset)
{
struct cr_img *img;
CoreEntry *core = item->core[0];
......@@ -727,7 +711,7 @@ static int dump_task_core_all(struct pstree_item *item,
core->tc->task_state = item->state;
core->tc->exit_code = 0;
ret = dump_thread_core(pid, core, &misc->ti);
ret = parasite_dump_thread_leader_seized(ctl, pid, core);
if (ret)
goto err;
......@@ -1327,18 +1311,12 @@ static int dump_one_task(struct pstree_item *item)
goto err_cure;
}
ret = dump_task_core_all(item, &pps_buf, &misc, cr_imgset);
ret = dump_task_core_all(parasite_ctl, item, &pps_buf, cr_imgset);
if (ret) {
pr_err("Dump core (pid: %d) failed with %d\n", pid, ret);
goto err_cure;
}
ret = dump_task_creds(parasite_ctl, cr_imgset);
if (ret) {
pr_err("Dump creds (pid: %d) failed with %d\n", pid, ret);
goto err;
}
ret = parasite_stop_daemon(parasite_ctl);
if (ret) {
pr_err("Can't cure (pid: %d) from parasite\n", pid);
......
......@@ -84,6 +84,7 @@ extern int __parasite_wait_daemon_ack(unsigned int cmd,
extern int parasite_dump_misc_seized(struct parasite_ctl *ctl, struct parasite_dump_misc *misc);
extern int parasite_dump_creds(struct parasite_ctl *ctl, struct _CredsEntry *ce);
extern int parasite_dump_thread_leader_seized(struct parasite_ctl *ctl, int pid, struct _CoreEntry *core);
extern int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
struct pid *tid, struct _CoreEntry *core);
extern int dump_thread_core(int pid, CoreEntry *core, const struct parasite_dump_thread *dt);
......
......@@ -43,7 +43,6 @@ enum {
PARASITE_CMD_DUMP_ITIMERS,
PARASITE_CMD_DUMP_POSIX_TIMERS,
PARASITE_CMD_DUMP_MISC,
PARASITE_CMD_DUMP_CREDS,
PARASITE_CMD_DRAIN_FDS,
PARASITE_CMD_GET_PROC_FD,
PARASITE_CMD_DUMP_TTY,
......@@ -151,14 +150,6 @@ static inline int posix_timers_dump_size(int timer_n)
return sizeof(int) + sizeof(struct posix_timer) * timer_n;
}
struct parasite_dump_thread {
unsigned int *tid_addr;
pid_t tid;
tls_t tls;
stack_t sas;
int pdeath_sig;
};
/*
* Misc sfuff, that is too small for separate file, but cannot
* be read w/o using parasite
......@@ -172,8 +163,6 @@ struct parasite_dump_misc {
u32 pgid;
u32 umask;
struct parasite_dump_thread ti;
int dumpable;
};
......@@ -182,9 +171,8 @@ struct parasite_dump_misc {
* and still fit the struct in one page
*/
#define PARASITE_MAX_GROUPS \
(PAGE_SIZE - \
offsetof(struct parasite_dump_creds, groups) \
) / sizeof(unsigned int) /* groups */
((PAGE_SIZE - sizeof(struct parasite_dump_thread) - \
offsetof(struct parasite_dump_creds, groups)) / sizeof(unsigned int)) /* groups */
struct parasite_dump_creds {
unsigned int cap_last_cap;
......@@ -214,6 +202,15 @@ struct parasite_dump_creds {
unsigned int groups[0];
};
struct parasite_dump_thread {
unsigned int *tid_addr;
pid_t tid;
tls_t tls;
stack_t sas;
int pdeath_sig;
struct parasite_dump_creds creds[0];
};
static inline void copy_sas(ThreadSasEntry *dst, const stack_t *src)
{
dst->ss_sp = encode_pointer(src->ss_sp);
......
......@@ -103,7 +103,8 @@ struct proc_status_creds {
u32 cap_bnd[PROC_CAP_SIZE];
};
bool proc_status_creds_eq(struct proc_status_creds *o1, struct proc_status_creds *o2);
bool proc_status_creds_dumpable(struct proc_status_creds *parent,
struct proc_status_creds *child);
typedef int (*mount_fn_t)(struct mount_info *mi, const char *src, const
char *fstype, unsigned long mountflags);
......
......@@ -532,12 +532,74 @@ err:
return -1;
}
static int alloc_groups_copy_creds(CredsEntry *ce, struct parasite_dump_creds *c)
{
BUILD_BUG_ON(sizeof(ce->groups[0]) != sizeof(c->groups[0]));
BUILD_BUG_ON(sizeof(ce->cap_inh[0]) != sizeof(c->cap_inh[0]));
BUILD_BUG_ON(sizeof(ce->cap_prm[0]) != sizeof(c->cap_prm[0]));
BUILD_BUG_ON(sizeof(ce->cap_eff[0]) != sizeof(c->cap_eff[0]));
BUILD_BUG_ON(sizeof(ce->cap_bnd[0]) != sizeof(c->cap_bnd[0]));
BUG_ON(ce->n_cap_inh != CR_CAP_SIZE);
BUG_ON(ce->n_cap_prm != CR_CAP_SIZE);
BUG_ON(ce->n_cap_eff != CR_CAP_SIZE);
BUG_ON(ce->n_cap_bnd != CR_CAP_SIZE);
memcpy(ce->cap_inh, c->cap_inh, sizeof(c->cap_inh[0]) * CR_CAP_SIZE);
memcpy(ce->cap_prm, c->cap_prm, sizeof(c->cap_prm[0]) * CR_CAP_SIZE);
memcpy(ce->cap_eff, c->cap_eff, sizeof(c->cap_eff[0]) * CR_CAP_SIZE);
memcpy(ce->cap_bnd, c->cap_bnd, sizeof(c->cap_bnd[0]) * CR_CAP_SIZE);
ce->secbits = c->secbits;
ce->n_groups = c->ngroups;
ce->groups = xmemdup(c->groups, sizeof(c->groups[0]) * c->ngroups);
ce->uid = c->uids[0];
ce->gid = c->gids[0];
ce->euid = c->uids[1];
ce->egid = c->gids[1];
ce->suid = c->uids[2];
ce->sgid = c->gids[2];
ce->fsuid = c->uids[3];
ce->fsgid = c->gids[3];
return ce->groups ? 0 : -ENOMEM;
}
int parasite_dump_thread_leader_seized(struct parasite_ctl *ctl, int pid, CoreEntry *core)
{
ThreadCoreEntry *tc = core->thread_core;
struct parasite_dump_thread *args;
struct parasite_dump_creds *pc;
int ret;
args = parasite_args(ctl, struct parasite_dump_thread);
pc = args->creds;
pc->cap_last_cap = kdat.last_cap;
ret = parasite_execute_daemon(PARASITE_CMD_DUMP_THREAD, ctl);
if (ret < 0)
return ret;
ret = alloc_groups_copy_creds(tc->creds, pc);
if (ret) {
pr_err("Can't copy creds for thread leader %d\n", pid);
return -1;
}
return dump_thread_core(pid, core, args);
}
int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
struct pid *tid, CoreEntry *core)
{
struct parasite_dump_thread *args;
pid_t pid = tid->real;
ThreadCoreEntry *tc = core->thread_core;
CredsEntry *creds = tc->creds;
struct parasite_dump_creds *pc;
int ret;
struct thread_ctx octx;
......@@ -545,6 +607,9 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
args = parasite_args(ctl, struct parasite_dump_thread);
pc = args->creds;
pc->cap_last_cap = kdat.last_cap;
ret = get_thread_ctx(pid, &octx);
if (ret)
return -1;
......@@ -559,6 +624,12 @@ int parasite_dump_thread_seized(struct parasite_ctl *ctl, int id,
return -1;
}
ret = alloc_groups_copy_creds(creds, pc);
if (ret) {
pr_err("Can't copy creds for thread %d\n", pid);
return -1;
}
ret = get_task_regs(pid, octx.regs, core);
if (ret) {
pr_err("Can't obtain regs for thread %d\n", pid);
......@@ -730,51 +801,6 @@ struct parasite_tty_args *parasite_dump_tty(struct parasite_ctl *ctl, int fd, in
return p;
}
int parasite_dump_creds(struct parasite_ctl *ctl, CredsEntry *ce)
{
struct parasite_dump_creds *pc;
BUILD_BUG_ON(sizeof(*pc) > PAGE_SIZE);
pc = parasite_args(ctl, struct parasite_dump_creds);
pc->cap_last_cap = kdat.last_cap;
if (parasite_execute_daemon(PARASITE_CMD_DUMP_CREDS, ctl) < 0)
return -1;
ce->n_cap_inh = CR_CAP_SIZE;
ce->cap_inh = pc->cap_inh;
ce->n_cap_prm = CR_CAP_SIZE;
ce->cap_prm = pc->cap_prm;
ce->n_cap_eff = CR_CAP_SIZE;
ce->cap_eff = pc->cap_eff;
ce->n_cap_bnd = CR_CAP_SIZE;
ce->cap_bnd = pc->cap_bnd;
ce->secbits = pc->secbits;
ce->n_groups = pc->ngroups;
/*
* Achtung! We leak the parasite args pointer to the caller.
* It's not safe in general, but in our case is OK, since the
* latter doesn't go to parasite before using the data in it.
*/
BUILD_BUG_ON(sizeof(ce->groups[0]) != sizeof(pc->groups[0]));
ce->groups = pc->groups;
ce->uid = pc->uids[0];
ce->gid = pc->gids[0];
ce->euid = pc->uids[1];
ce->egid = pc->gids[1];
ce->suid = pc->uids[2];
ce->sgid = pc->gids[2];
ce->fsuid = pc->uids[3];
ce->fsgid = pc->gids[3];
return 0;
}
int parasite_drain_fds_seized(struct parasite_ctl *ctl,
struct parasite_drain_fd *dfds, int *lfds, struct fd_opts *opts)
{
......
......@@ -145,6 +145,8 @@ static int dump_posix_timers(struct parasite_dump_posix_timers_args *args)
return ret;
}
static int dump_creds(struct parasite_dump_creds *args);
static int dump_thread_common(struct parasite_dump_thread *ti)
{
int ret;
......@@ -159,6 +161,10 @@ static int dump_thread_common(struct parasite_dump_thread *ti)
goto out;
ret = sys_prctl(PR_GET_PDEATHSIG, (unsigned long)&ti->pdeath_sig, 0, 0, 0);
if (ret)
goto out;
ret = dump_creds(ti->creds);
out:
return ret;
}
......@@ -174,7 +180,7 @@ static int dump_misc(struct parasite_dump_misc *args)
sys_umask(args->umask); /* never fails */
args->dumpable = sys_prctl(PR_GET_DUMPABLE, 0, 0, 0, 0);
return dump_thread_common(&args->ti);
return 0;
}
static int dump_creds(struct parasite_dump_creds *args)
......@@ -597,12 +603,12 @@ static noinline __used int noinline parasite_daemon(void *args)
case PARASITE_CMD_DUMP_POSIX_TIMERS:
ret = dump_posix_timers(args);
break;
case PARASITE_CMD_DUMP_THREAD:
ret = dump_thread(args);
break;
case PARASITE_CMD_DUMP_MISC:
ret = dump_misc(args);
break;
case PARASITE_CMD_DUMP_CREDS:
ret = dump_creds(args);
break;
case PARASITE_CMD_DRAIN_FDS:
ret = drain_fds(args);
break;
......
......@@ -2221,13 +2221,53 @@ int aufs_parse(struct mount_info *new)
return ret;
}
bool proc_status_creds_eq(struct proc_status_creds *o1, struct proc_status_creds *o2)
bool proc_status_creds_dumpable(struct proc_status_creds *parent,
struct proc_status_creds *child)
{
/* FIXME: this is a little too strict, we should do semantic comparison
* of seccomp filters instead of forcing them to be exactly identical.
* It's not unsafe, though, so let's be lazy for now.
const size_t size = sizeof(struct proc_status_creds) -
offsetof(struct proc_status_creds, cap_inh);
/*
* The comparision rules are the following
*
* - CAPs can be different
* - seccomp filters should be passed via
* semantic comparision (FIXME) but for
* now we require them to be exactly
* identical
* - the rest of members must match
*/
return memcmp(o1, o2, sizeof(struct proc_status_creds)) == 0;
if (memcmp(parent, child, size)) {
if (!pr_quelled(LOG_DEBUG)) {
pr_debug("Creds undumpable (parent:child)\n"
" uids: %d:%d %d:%d %d:%d %d:%d\n"
" gids: %d:%d %d:%d %d:%d %d:%d\n"
" state: %d:%d"
" ppid: %d:%d\n"
" sigpnd: %llu:%llu\n"
" shdpnd: %llu:%llu\n"
" seccomp_mode: %d:%d\n"
" last_filter: %u:%u\n",
parent->uids[0], child->uids[0],
parent->uids[1], child->uids[1],
parent->uids[2], child->uids[2],
parent->uids[3], child->uids[3],
parent->gids[0], child->gids[0],
parent->gids[1], child->gids[1],
parent->gids[2], child->gids[2],
parent->gids[3], child->gids[3],
parent->state, child->state,
parent->ppid, child->ppid,
parent->sigpnd, child->sigpnd,
parent->shdpnd, child->shdpnd,
parent->seccomp_mode, child->seccomp_mode,
parent->last_filter, child->last_filter);
}
return false;
}
return true;
}
int parse_children(pid_t pid, pid_t **_c, int *_n)
......
......@@ -247,7 +247,7 @@ try_again:
**creds = cr;
} else if (!proc_status_creds_eq(*creds, &cr)) {
} else if (!proc_status_creds_dumpable(*creds, &cr)) {
pr_err("creds don't match %d %d\n", pid, ppid);
goto err;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment