test_switch_id.c (6597B)
1 /* Copyright (c) 2015-2021, The Tor Project, Inc. */ 2 /* See LICENSE for licensing information */ 3 4 #include "core/or/or.h" 5 #include "lib/process/setuid.h" 6 7 #ifdef HAVE_SYS_CAPABILITY_H 8 #include <sys/capability.h> 9 #endif 10 #ifdef HAVE_UNISTD_H 11 #include <unistd.h> 12 #endif 13 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 20 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 } 32 }; 33 34 #if !defined(_WIN32) 35 36 /* Returns the first port that we think we can bind to without special 37 * permissions. Usually this function returns 1024. */ 38 static uint16_t 39 unprivileged_port_range_start(void) 40 { 41 uint16_t result = 1024; 42 43 #if defined(__linux__) 44 char *content = NULL; 45 46 content = read_file_to_str( 47 "/proc/sys/net/ipv4/ip_unprivileged_port_start", 48 0, 49 NULL); 50 51 if (content != NULL) { 52 int ok = 1; 53 uint16_t tmp_result; 54 55 tmp_result = (uint16_t)tor_parse_long(content, 10, 0, 65535, &ok, NULL); 56 57 if (ok) { 58 result = tmp_result; 59 } else { 60 fprintf(stderr, 61 "Unable to convert ip_unprivileged_port_start to integer: %s\n", 62 content); 63 } 64 } 65 66 tor_free(content); 67 #endif /* defined(__linux__) */ 68 69 return result; 70 } 71 72 #define PORT_TEST_RANGE_START 600 73 #define PORT_TEST_RANGE_END 1024 74 75 /* 0 on no, 1 on yes, -1 on failure. */ 76 static int 77 check_can_bind_low_ports(void) 78 { 79 int port; 80 struct sockaddr_in sin; 81 memset(&sin, 0, sizeof(sin)); 82 sin.sin_family = AF_INET; 83 84 for (port = PORT_TEST_RANGE_START; port < PORT_TEST_RANGE_END; ++port) { 85 sin.sin_port = htons(port); 86 tor_socket_t fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 87 if (! SOCKET_OK(fd)) { 88 perror("socket"); 89 return -1; 90 } 91 92 int one = 1; 93 if (setsockopt(fd, SOL_SOCKET,SO_REUSEADDR, (void*)&one, 94 (socklen_t)sizeof(one))) { 95 perror("setsockopt"); 96 tor_close_socket_simple(fd); 97 return -1; 98 } 99 100 int res = bind(fd, (struct sockaddr *)&sin, sizeof(sin)); 101 tor_close_socket_simple(fd); 102 103 if (res == 0) { 104 /* bind was successful */ 105 return 1; 106 } else if (errno == EACCES || errno == EPERM) { 107 /* Got a permission-denied error. */ 108 return 0; 109 } else if (errno == EADDRINUSE) { 110 /* Huh; somebody is using that port. */ 111 } else { 112 perror("bind"); 113 } 114 } 115 116 return -1; 117 } 118 #endif /* !defined(_WIN32) */ 119 120 int 121 main(int argc, char **argv) 122 { 123 #if defined(_WIN32) 124 (void) argc; 125 (void) argv; 126 (void) which_test; 127 128 fprintf(stderr, "This test is not supported on your OS.\n"); 129 return 77; 130 #else /* !defined(_WIN32) */ 131 const char *username; 132 const char *testname; 133 if (argc != 3) { 134 fprintf(stderr, "I want 2 arguments: a username and a command.\n"); 135 return 1; 136 } 137 if (getuid() != 0) { 138 fprintf(stderr, "This test only works when it's run as root.\n"); 139 return 1; 140 } 141 username = argv[1]; 142 testname = argv[2]; 143 int test_id = -1; 144 int i; 145 for (i = 0; which_test[i].name; ++i) { 146 if (!strcmp(which_test[i].name, testname)) { 147 test_id = which_test[i].test_id; 148 break; 149 } 150 } 151 if (test_id == -1) { 152 fprintf(stderr, "Unrecognized test '%s'\n", testname); 153 return 1; 154 } 155 156 #ifdef HAVE_LINUX_CAPABILITIES 157 const int have_cap_support = 1; 158 #else 159 const int have_cap_support = 0; 160 #endif 161 162 int okay; 163 164 init_logging(1); 165 log_severity_list_t sev; 166 memset(&sev, 0, sizeof(sev)); 167 set_log_severity_config(LOG_WARN, LOG_ERR, &sev); 168 add_stream_log(&sev, "", fileno(stderr)); 169 170 switch (test_id) 171 { 172 case TEST_BUILT_WITH_CAPS: 173 /* Succeed if we were built with capability support. */ 174 okay = have_cap_support; 175 break; 176 case TEST_HAVE_CAPS: 177 /* Succeed if "capabilities work" == "we were built with capability 178 * support." */ 179 okay = have_cap_support == have_capability_support(); 180 break; 181 case TEST_ROOT_CAN_BIND_LOW: 182 /* Succeed if root can bind low ports. */ 183 okay = check_can_bind_low_ports() == 1; 184 break; 185 case TEST_SETUID: 186 /* Succeed if we can do a setuid with no capability retention, and doing 187 * so makes us lose the ability to bind low ports */ 188 case TEST_SETUID_KEEPCAPS: 189 /* Succeed if we can do a setuid with capability retention, and doing so 190 * does not make us lose the ability to bind low ports */ 191 { 192 const int keepcaps = (test_id == TEST_SETUID_KEEPCAPS); 193 okay = switch_id(username, keepcaps ? SWITCH_ID_KEEP_BINDLOW : 0) == 0; 194 195 if (okay) { 196 /* Only run this check if there are ports we may not be able to bind 197 * to. */ 198 const uint16_t min_port = unprivileged_port_range_start(); 199 200 if (min_port >= PORT_TEST_RANGE_START && 201 min_port < PORT_TEST_RANGE_END) { 202 okay = check_can_bind_low_ports() == keepcaps; 203 } else { 204 fprintf(stderr, 205 "Skipping check for whether we can bind to any " 206 "privileged ports as the user system seems to " 207 "allow us to bind to ports even without any " 208 "capabilities set.\n"); 209 } 210 } 211 break; 212 } 213 case TEST_SETUID_STRICT: 214 /* Succeed if, after a setuid, we cannot setuid back, and we cannot 215 * re-grab any capabilities. */ 216 okay = switch_id(username, SWITCH_ID_KEEP_BINDLOW) == 0; 217 if (okay) { 218 /* We'd better not be able to setuid back! */ 219 if (setuid(0) == 0 || errno != EPERM) { 220 okay = 0; 221 } 222 } 223 #ifdef HAVE_LINUX_CAPABILITIES 224 if (okay) { 225 cap_t caps = cap_get_proc(); 226 const cap_value_t caplist[] = { 227 CAP_SETUID, 228 }; 229 cap_set_flag(caps, CAP_PERMITTED, 1, caplist, CAP_SET); 230 if (cap_set_proc(caps) == 0 || errno != EPERM) { 231 okay = 0; 232 } 233 cap_free(caps); 234 } 235 #endif /* defined(HAVE_LINUX_CAPABILITIES) */ 236 break; 237 default: 238 fprintf(stderr, "Unsupported test '%s'\n", testname); 239 okay = 0; 240 break; 241 } 242 243 if (!okay) { 244 fprintf(stderr, "Test %s failed!\n", testname); 245 } 246 247 return (okay ? 0 : 1); 248 #endif /* defined(_WIN32) */ 249 }