Bump copyright date to 2019
[tor.git] / src / test / test_switch_id.c
blobbaddf8d66e1aa25ba052996c9a4c189cf9cb60a8
1 /* Copyright (c) 2015-2019, The Tor Project, Inc. */
2 /* See LICENSE for licensing information */
4 #include "core/or/or.h"
5 #include "lib/process/setuid.h"
7 #ifdef HAVE_SYS_CAPABILITY_H
8 #include <sys/capability.h>
9 #endif
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
14 #define TEST_BUILT_WITH_CAPS 0
15 #define TEST_HAVE_CAPS 1
16 #define TEST_ROOT_CAN_BIND_LOW 2
17 #define TEST_SETUID 3
18 #define TEST_SETUID_KEEPCAPS 4
19 #define TEST_SETUID_STRICT 5
21 static const struct {
22 const char *name;
23 int test_id;
24 } which_test[] = {
25 { "built-with-caps", TEST_BUILT_WITH_CAPS },
26 { "have-caps", TEST_HAVE_CAPS },
27 { "root-bind-low", TEST_ROOT_CAN_BIND_LOW },
28 { "setuid", TEST_SETUID },
29 { "setuid-keepcaps", TEST_SETUID_KEEPCAPS },
30 { "setuid-strict", TEST_SETUID_STRICT },
31 { NULL, 0 }
34 #if !defined(_WIN32)
35 /* 0 on no, 1 on yes, -1 on failure. */
36 static int
37 check_can_bind_low_ports(void)
39 int port;
40 struct sockaddr_in sin;
41 memset(&sin, 0, sizeof(sin));
42 sin.sin_family = AF_INET;
44 for (port = 600; port < 1024; ++port) {
45 sin.sin_port = htons(port);
46 tor_socket_t fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
47 if (! SOCKET_OK(fd)) {
48 perror("socket");
49 return -1;
52 int one = 1;
53 if (setsockopt(fd, SOL_SOCKET,SO_REUSEADDR, (void*)&one,
54 (socklen_t)sizeof(one))) {
55 perror("setsockopt");
56 tor_close_socket_simple(fd);
57 return -1;
60 int res = bind(fd, (struct sockaddr *)&sin, sizeof(sin));
61 tor_close_socket_simple(fd);
63 if (res == 0) {
64 /* bind was successful */
65 return 1;
66 } else if (errno == EACCES || errno == EPERM) {
67 /* Got a permission-denied error. */
68 return 0;
69 } else if (errno == EADDRINUSE) {
70 /* Huh; somebody is using that port. */
71 } else {
72 perror("bind");
76 return -1;
78 #endif /* !defined(_WIN32) */
80 int
81 main(int argc, char **argv)
83 #if defined(_WIN32)
84 (void) argc;
85 (void) argv;
86 (void) which_test;
88 fprintf(stderr, "This test is not supported on your OS.\n");
89 return 77;
90 #else /* !(defined(_WIN32)) */
91 const char *username;
92 const char *testname;
93 if (argc != 3) {
94 fprintf(stderr, "I want 2 arguments: a username and a command.\n");
95 return 1;
97 if (getuid() != 0) {
98 fprintf(stderr, "This test only works when it's run as root.\n");
99 return 1;
101 username = argv[1];
102 testname = argv[2];
103 int test_id = -1;
104 int i;
105 for (i = 0; which_test[i].name; ++i) {
106 if (!strcmp(which_test[i].name, testname)) {
107 test_id = which_test[i].test_id;
108 break;
111 if (test_id == -1) {
112 fprintf(stderr, "Unrecognized test '%s'\n", testname);
113 return 1;
116 #ifdef HAVE_LINUX_CAPABILITIES
117 const int have_cap_support = 1;
118 #else
119 const int have_cap_support = 0;
120 #endif
122 int okay;
124 init_logging(1);
125 log_severity_list_t sev;
126 memset(&sev, 0, sizeof(sev));
127 set_log_severity_config(LOG_WARN, LOG_ERR, &sev);
128 add_stream_log(&sev, "", fileno(stderr));
130 switch (test_id)
132 case TEST_BUILT_WITH_CAPS:
133 /* Succeed if we were built with capability support. */
134 okay = have_cap_support;
135 break;
136 case TEST_HAVE_CAPS:
137 /* Succeed if "capabilities work" == "we were built with capability
138 * support." */
139 okay = have_cap_support == have_capability_support();
140 break;
141 case TEST_ROOT_CAN_BIND_LOW:
142 /* Succeed if root can bind low ports. */
143 okay = check_can_bind_low_ports() == 1;
144 break;
145 case TEST_SETUID:
146 /* Succeed if we can do a setuid with no capability retention, and doing
147 * so makes us lose the ability to bind low ports */
148 case TEST_SETUID_KEEPCAPS:
149 /* Succeed if we can do a setuid with capability retention, and doing so
150 * does not make us lose the ability to bind low ports */
152 int keepcaps = (test_id == TEST_SETUID_KEEPCAPS);
153 okay = switch_id(username, keepcaps ? SWITCH_ID_KEEP_BINDLOW : 0) == 0;
154 if (okay) {
155 okay = check_can_bind_low_ports() == keepcaps;
157 break;
159 case TEST_SETUID_STRICT:
160 /* Succeed if, after a setuid, we cannot setuid back, and we cannot
161 * re-grab any capabilities. */
162 okay = switch_id(username, SWITCH_ID_KEEP_BINDLOW) == 0;
163 if (okay) {
164 /* We'd better not be able to setuid back! */
165 if (setuid(0) == 0 || errno != EPERM) {
166 okay = 0;
169 #ifdef HAVE_LINUX_CAPABILITIES
170 if (okay) {
171 cap_t caps = cap_get_proc();
172 const cap_value_t caplist[] = {
173 CAP_SETUID,
175 cap_set_flag(caps, CAP_PERMITTED, 1, caplist, CAP_SET);
176 if (cap_set_proc(caps) == 0 || errno != EPERM) {
177 okay = 0;
179 cap_free(caps);
181 #endif /* defined(HAVE_LINUX_CAPABILITIES) */
182 break;
183 default:
184 fprintf(stderr, "Unsupported test '%s'\n", testname);
185 okay = 0;
186 break;
189 if (!okay) {
190 fprintf(stderr, "Test %s failed!\n", testname);
193 return (okay ? 0 : 1);
194 #endif /* defined(_WIN32) */