summaryrefslogtreecommitdiff
path: root/internal/namespace/launcher_src/launcher.c
blob: 60c655821e503d2d916e1da3eeb84aa3d06e1799 (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
#define _GNU_SOURCE
#include <sched.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <grp.h>

int main(int argc, char **argv) {
    if (argc < 1) {
        fprintf(stderr, "Usage: launcher <command> [args...]\n");
        return 1;
    }

    // Check if we are joining an existing namespace
    char *join_pid_str = getenv("WG_WRAP_JOIN_PID");
    if (join_pid_str != NULL && strlen(join_pid_str) > 0) {
        int target_pid = atoi(join_pid_str);
        if (target_pid > 0) {
            char path[128];
            int fd;

            // 1. Join User Namespace first
            snprintf(path, sizeof(path), "/proc/%d/ns/user", target_pid);
            fd = open(path, O_RDONLY);
            if (fd == -1) {
                perror("open target user namespace");
                return 1;
            }
            if (setns(fd, CLONE_NEWUSER) == -1) {
                perror("setns CLONE_NEWUSER");
                close(fd);
                return 1;
            }
            close(fd);

            // 2. Join Mount Namespace
            snprintf(path, sizeof(path), "/proc/%d/ns/mnt", target_pid);
            fd = open(path, O_RDONLY);
            if (fd == -1) {
                perror("open target mount namespace");
                return 1;
            }
            if (setns(fd, CLONE_NEWNS) == -1) {
                perror("setns CLONE_NEWNS");
                close(fd);
                return 1;
            }
            close(fd);

            // 3. Join Network Namespace
            snprintf(path, sizeof(path), "/proc/%d/ns/net", target_pid);
            fd = open(path, O_RDONLY);
            if (fd == -1) {
                perror("open target network namespace");
                return 1;
            }
            if (setns(fd, CLONE_NEWNET) == -1) {
                perror("setns CLONE_NEWNET");
                close(fd);
                return 1;
            }
            close(fd);

            // Execute the target command
            if (argv[0] == NULL) {
                fprintf(stderr, "No target binary provided in argv[0]\n");
                return 1;
            }
            if (execv(argv[0], argv) == -1) {
                perror("execv failed");
                return 1;
            }
            return 0;
        }
    }

    // 1. Capture host identities BEFORE unsharing
    uid_t current_uid = getuid();
    gid_t current_gid = getgid();

    // 2. Combined Unshare for User, Mount, and Network namespaces
    // We unshare Mount namespace (CLONE_NEWNS) to allow private /etc/resolv.conf setup
    // without contaminating the host filesystem.
    if (unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWNET) == -1) {
        perror("unshare(CLONE_NEWUSER | CLONE_NEWNS | CLONE_NEWNET)");
        return 1;
    }

    char map[64];

    // 3. Write UID map
    snprintf(map, sizeof(map), "0 %u 1\n", current_uid);
    int fd = open("/proc/self/uid_map", O_WRONLY);
    if (fd == -1) {
        perror("open uid_map");
        return 1;
    }
    if (write(fd, map, strlen(map)) == -1) {
        perror("write uid_map");
        close(fd);
        return 1;
    }
    close(fd);

    // 4. Disable setgroups
    fd = open("/proc/self/setgroups", O_WRONLY);
    if (fd != -1) {
        write(fd, "deny", 4);
        close(fd);
    }

    // 5. Write GID map
    snprintf(map, sizeof(map), "0 %u 1\n", current_gid);
    fd = open("/proc/self/gid_map", O_WRONLY);
    if (fd == -1) {
        perror("open gid_map");
        return 1;
    }
    if (write(fd, map, strlen(map)) == -1) {
        perror("write gid_map");
        close(fd);
        return 1;
    }
    close(fd);

    // 6. Execute the target command
    // In this architecture, the Go Bootstrap code passes the target binary 
    // as the first element of the argv array.
    // Therefore, argv[0] is the path to the binary we want to execute.
    if (argv[0] == NULL) {
        fprintf(stderr, "No target binary provided in argv[0]\\n");
        return 1;
    }

    // Prepare a new argv for the target command.
    // We want the target binary to see itself as argv[0].
    // The current argv is [target_binary, arg1, arg2, ...].
    // execv expects argv[0] to be the filename, and the rest as arguments.
    // This is already the case here, but let's be explicit.
    if (execv(argv[0], argv) == -1) {
        perror("execv failed");
        return 1;
    }
    
    return 0;
}