aboutsummaryrefslogtreecommitdiff
path: root/toys/other/nsenter.c
blob: dfcfbe83220dfd39a9ddafc9607efe038f3e1abe (plain)
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
/* nsenter.c - Enter existing namespaces
 *
 * Copyright 2014 Andy Lutomirski <luto@amacapital.net>
 *
 * See http://man7.org/linux/man-pages/man1/nsenter.1.html
 *
 * unshare.c - run command in new context
 *
 * Copyright 2011 Rob Landley <rob@landley.net>
 *
 * See http://man7.org/linux/man-pages/man1/unshare.1.html
 *

// Note: flags go in same order (right to left) for shared subset
USE_NSENTER(NEWTOY(nsenter, "<1F(no-fork)t#<1(target)i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))
USE_UNSHARE(NEWTOY(unshare, "<1^f(fork);r(map-root-user);i:(ipc);m:(mount);n:(net);p:(pid);u:(uts);U:(user);", TOYFLAG_USR|TOYFLAG_BIN))

config UNSHARE
  bool "unshare"
  default y
  depends on TOYBOX_CONTAINER
  help
    usage: unshare [-imnpuUr] COMMAND...

    Create new container namespace(s) for this process and its children, so
    some attribute is not shared with the parent process.

    -f	Fork command in the background (--fork)
    -i	SysV IPC (message queues, semaphores, shared memory) (--ipc)
    -m	Mount/unmount tree (--mount)
    -n	Network address, sockets, routing, iptables (--net)
    -p	Process IDs and init (--pid)
    -r	Become root (map current euid/egid to 0/0, implies -U) (--map-root-user)
    -u	Host and domain names (--uts)
    -U	UIDs, GIDs, capabilities (--user)

    A namespace allows a set of processes to have a different view of the
    system than other sets of processes.

config NSENTER
  bool "nsenter"
  depends on TOYBOX_CONTAINER
  default y
  help
    usage: nsenter [-t pid] [-F] [-i] [-m] [-n] [-p] [-u] [-U] COMMAND...

    Run COMMAND in an existing (set of) namespace(s).

    -t	PID to take namespaces from    (--target)
    -F	don't fork, even if -p is used (--no-fork)

    The namespaces to switch are:

    -i	SysV IPC: message queues, semaphores, shared memory (--ipc)
    -m	Mount/unmount tree (--mount)
    -n	Network address, sockets, routing, iptables (--net)
    -p	Process IDs and init, will fork unless -F is used (--pid)
    -u	Host and domain names (--uts)
    -U	UIDs, GIDs, capabilities (--user)

    If -t isn't specified, each namespace argument must provide a path
    to a namespace file, ala "-i=/proc/$PID/ns/ipc"
*/

#define FOR_nsenter
#include "toys.h"
#include <sys/syscall.h>
#include <linux/sched.h>

#define unshare(flags) syscall(SYS_unshare, flags)
#define setns(fd, nstype) syscall(SYS_setns, fd, nstype)

GLOBALS(
  char *Uupnmi[6];
  long t;
)

// Code that must run in unshare's flag context
#define CLEANUP_nsenter
#define FOR_unshare
#include <generated/flags.h>

static void write_ugid_map(char *map, unsigned eugid)
{
  int bytes = sprintf(toybuf, "0 %u 1", eugid), fd = xopen(map, O_WRONLY);

  xwrite(fd, toybuf, bytes);
  xclose(fd);
}

static void handle_r(int euid, int egid)
{
  int fd;

  if ((fd = open("/proc/self/setgroups", O_WRONLY)) >= 0) {
    xwrite(fd, "deny", 4);
    close(fd);
  }

  write_ugid_map("/proc/self/uid_map", euid);
  write_ugid_map("/proc/self/gid_map", egid);
}

static int test_r()
{
  return toys.optflags & FLAG_r;
}

static int test_f()
{
  return toys.optflags & FLAG_f;
}

// Shift back to the context GLOBALS lives in (I.E. matching the filename).
#define CLEANUP_unshare
#define FOR_nsenter
#include <generated/flags.h>

void unshare_main(void)
{
  unsigned flags[]={CLONE_NEWUSER, CLONE_NEWUTS, CLONE_NEWPID, CLONE_NEWNET,
                    CLONE_NEWNS, CLONE_NEWIPC}, f = 0;
  int i, fd;

  // Create new namespace(s)?
  if (CFG_UNSHARE && *toys.which->name=='u') {
    // For -r, we have to save our original [ug]id before calling unshare()
    int euid = geteuid(), egid = getegid();

    // unshare -U does not imply -r, so we cannot use [+rU]
    if (test_r()) toys.optflags |= FLAG_U;

    for (i = 0; i<ARRAY_LEN(flags); i++)
      if (toys.optflags & (1<<i)) f |= flags[i];

    if (unshare(f)) perror_exit(0);
    if (test_r()) handle_r(euid, egid);

    if (test_f()) {
      toys.exitval = xrun(toys.optargs);

      return;
    }
  // Bind to existing namespace(s)?
  } else if (CFG_NSENTER) {
    char *nsnames = "user\0uts\0pid\0net\0mnt\0ipc";

    for (i = 0; i<ARRAY_LEN(flags); i++) {
      char *filename = TT.Uupnmi[i];

      if (toys.optflags & (1<<i)) {
        if (!filename || !*filename) {
          if (!(toys.optflags & FLAG_t)) error_exit("need -t or =filename");
          sprintf(toybuf, "/proc/%ld/ns/%s", TT.t, nsnames);
          filename = toybuf;
        }

        if (setns(fd = xopenro(filename), flags[i])) perror_exit("setns");
        close(fd);
      }
      nsnames += strlen(nsnames)+1;
    }

    if ((toys.optflags & FLAG_p) && !(toys.optflags & FLAG_F)) {
      toys.exitval = xrun(toys.optargs);

      return;
    }
  }

  xexec(toys.optargs);
}

void nsenter_main(void)
{
  unshare_main();
}