forked from pekman/netns-exec
-
Notifications
You must be signed in to change notification settings - Fork 0
/
netns-exec.c
138 lines (113 loc) · 3.3 KB
/
netns-exec.c
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
/*
* Copyright (C) 2016 Pekka Ekman
*
* 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.
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <string.h>
#include <stdbool.h>
#include "namespace.h"
#define EXIT_INVALID_ARGS 2
#define EXIT_NOT_FOUND 127
#define EXIT_CANNOT_EXEC 126
#define EXIT_OTHER_ERROR 125
static void usage(const char *argv0, bool is_error)
{
fprintf(
stderr,
"\n"
"usage:\n"
"\n"
"run command in netns:\n"
" %1$s [-h | --help] [--] <netns> <command> [<args> ...]\n"
"\n"
"run shell in netns:\n"
" %1$s [-h | --help] [--] <netns>\n"
"\n",
argv0);
exit(is_error ? EXIT_INVALID_ARGS : 0);
}
/*
* Check if namespace name is valid.
*
* The iproute2 code doesn't seem to do any validation. That is
* probably because it's normally run as root, so the lack of
* validation doesn't allow the user to do anything they couldn't do
* already. This is not true for a suid root program, so we have to do
* the validation.
*
* Because the namespace is implemented as a file, the character '/'
* and the names ".", ".." and "" are forbidden. (Character '\0' is
* also forbidden, but cannot be passed in a command line argument.)
*/
static bool is_valid_ns(const char *ns)
{
return ns[0] != '\0'
&& strcmp(ns, ".") != 0 && strcmp(ns, "..") != 0
&& strchr(ns, '/') == NULL;
}
static void drop_root_privileges(void)
{
if (setuid(getuid()) < 0) {
perror("error setting user id");
exit(EXIT_OTHER_ERROR);
}
if (setgid(getgid()) < 0) {
perror("error setting group id");
exit(EXIT_OTHER_ERROR);
}
}
int main(int argc, char *argv[])
{
// parse arguments
char *argv0 = argv[0];
if (argc < 2 || (argc == 2 && strcmp(argv[1], "--") == 0))
// no netns given
usage(argv0, true);
if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-h") == 0)
usage(argv0, false);
if (strcmp(argv[1], "--") == 0) {
argv++;
argc--;
}
else if (argv[1][0] == '-') {
// invalid command line option
fprintf(stderr, "Unknown option: %s\n", argv[1]);
usage(argv0, true);
}
if (! is_valid_ns(argv[1])) {
fputs("Invalid namespace\n", stderr);
usage(argv0, true);
}
// switch network namespace using iproute2's function
if (netns_switch(argv[1]) < 0)
return EXIT_OTHER_ERROR;
drop_root_privileges();
char **command_argv;
// if no command given, start shell
if (argc == 2) {
static char *shell_argv[] = { "/bin/sh", NULL };
char *shell = getenv("SHELL");
if (shell != NULL)
shell_argv[0] = shell;
command_argv = shell_argv;
}
else {
command_argv = &argv[2];
}
// execute the command
execvp(command_argv[0], command_argv);
fprintf(stderr, "error executing \"%s\": %s",
command_argv[0], strerror(errno));
if (errno == ENOENT)
return EXIT_NOT_FOUND;
else
return EXIT_CANNOT_EXEC;
}