New 'DisableNetwork' option to prevent Tor from using the network

Some controllers want this so they can mess with Tor's configuration
for a while via the control port before actually letting Tor out of
the house.

We do this with a new DisableNetwork option, that prevents Tor from
making any outbound connections or binding any non-control
listeners.  Additionally, it shuts down the same functionality as
shuts down when we are hibernating, plus the code that launches
directory downloads.

To make sure I didn't miss anything, I added a clause straight to
connection_connect, so that we won't even try to open an outbound
socket when the network is disabled.  In my testing, I made this an
assert, but since I probably missed something, I've turned it into a
BUG warning for testing.
This commit is contained in:
Nick Mathewson 2011-11-28 15:44:10 -05:00
parent b5a306e82c
commit df9b76460c
10 changed files with 122 additions and 32 deletions

9
changes/disable_network Normal file
View File

@ -0,0 +1,9 @@
o Minor features:
- New "DisableNetwork" option to prevent Tor from launching any
connections or accepting any connections except on a control
port. Some bundles and controllers want to use this so they can
configure Tor before letting Tor talk to the rest of the
network--for example, to prevent any connections from being made
to a non-bridge address.

View File

@ -231,6 +231,7 @@ static config_var_t _option_vars[] = {
V(CountPrivateBandwidth, BOOL, "0"), V(CountPrivateBandwidth, BOOL, "0"),
V(DataDirectory, FILENAME, NULL), V(DataDirectory, FILENAME, NULL),
OBSOLETE("DebugLogFile"), OBSOLETE("DebugLogFile"),
V(DisableNetwork, BOOL, "0"),
V(DirAllowPrivateAddresses, BOOL, NULL), V(DirAllowPrivateAddresses, BOOL, NULL),
V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"), V(TestingAuthDirTimeToLearnReachability, INTERVAL, "30 minutes"),
V(DirListenAddress, LINELIST, NULL), V(DirListenAddress, LINELIST, NULL),
@ -1093,13 +1094,19 @@ options_act_reversible(const or_options_t *old_options, char **msg)
consider_hibernation(time(NULL)); consider_hibernation(time(NULL));
/* Launch the listeners. (We do this before we setuid, so we can bind to /* Launch the listeners. (We do this before we setuid, so we can bind to
* ports under 1024.) We don't want to rebind if we're hibernating. */ * ports under 1024.) We don't want to rebind if we're hibernating. If
* networking is disabled, this will close all but the control listeners,
* but disable those. */
if (!we_are_hibernating()) { if (!we_are_hibernating()) {
if (retry_all_listeners(replaced_listeners, new_listeners) < 0) { if (retry_all_listeners(replaced_listeners, new_listeners) < 0) {
*msg = tor_strdup("Failed to bind one of the listener ports."); *msg = tor_strdup("Failed to bind one of the listener ports.");
goto rollback; goto rollback;
} }
} }
if (options->DisableNetwork) {
/* Aggressively close non-controller stuff, NOW */
connection_mark_all_noncontrol_connections();
}
} }
#if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H) #if defined(HAVE_NET_IF_H) && defined(HAVE_NET_PFVAR_H)
@ -4094,6 +4101,7 @@ options_transition_affects_descriptor(const or_options_t *old_options,
old_options->ORPort != new_options->ORPort || old_options->ORPort != new_options->ORPort ||
old_options->DirPort != new_options->DirPort || old_options->DirPort != new_options->DirPort ||
old_options->ClientOnly != new_options->ClientOnly || old_options->ClientOnly != new_options->ClientOnly ||
old_options->DisableNetwork != new_options->DisableNetwork ||
old_options->_PublishServerDescriptor != old_options->_PublishServerDescriptor !=
new_options->_PublishServerDescriptor || new_options->_PublishServerDescriptor ||
get_effective_bwrate(old_options) != get_effective_bwrate(new_options) || get_effective_bwrate(old_options) != get_effective_bwrate(new_options) ||

View File

@ -1318,6 +1318,24 @@ connection_connect(connection_t *conn, const char *address,
else else
protocol_family = PF_INET; protocol_family = PF_INET;
if (get_options()->DisableNetwork) {
/* We should never even try to connect anyplace if DisableNetwork is set.
* Warn if we do, and refuse to make the connection. */
static ratelim_t disablenet_violated = RATELIM_INIT(30*60);
char *m;
#ifdef MS_WINDOWS
*socket_error = WSAENETUNREACH;
#else
*socket_error = ENETUNREACH;
#endif
if ((m = rate_limit_log(&disablenet_violated, approx_time()))) {
log_warn(LD_BUG, "Tried to open a socket with DisableNetwork set.%s", m);
tor_free(m);
}
tor_fragile_assert();
return -1;
}
s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP); s = tor_open_socket(protocol_family,SOCK_STREAM,IPPROTO_TCP);
if (s < 0) { if (s < 0) {
*socket_error = tor_socket_errno(-1); *socket_error = tor_socket_errno(-1);
@ -1968,7 +1986,7 @@ retry_all_listeners(smartlist_t *replaced_conns,
smartlist_add(listeners, conn); smartlist_add(listeners, conn);
} SMARTLIST_FOREACH_END(conn); } SMARTLIST_FOREACH_END(conn);
if (! options->ClientOnly) { if (! options->ClientOnly && ! options->DisableNetwork) {
if (retry_listeners(listeners, if (retry_listeners(listeners,
CONN_TYPE_OR_LISTENER, options->ORListenAddress, CONN_TYPE_OR_LISTENER, options->ORListenAddress,
options->ORPort, "0.0.0.0", options->ORPort, "0.0.0.0",
@ -1981,10 +1999,13 @@ retry_all_listeners(smartlist_t *replaced_conns,
retval = -1; retval = -1;
} }
if (!options->DisableNetwork) {
if (retry_listener_ports(listeners, if (retry_listener_ports(listeners,
get_configured_client_ports(), get_configured_client_ports(),
new_conns) < 0) new_conns) < 0)
retval = -1; retval = -1;
}
if (retry_listeners(listeners, if (retry_listeners(listeners,
CONN_TYPE_CONTROL_LISTENER, CONN_TYPE_CONTROL_LISTENER,
options->ControlListenAddress, options->ControlListenAddress,
@ -2025,6 +2046,43 @@ retry_all_listeners(smartlist_t *replaced_conns,
return retval; return retval;
} }
/** DOCDOC */
void
connection_mark_all_noncontrol_listeners(void)
{
SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
if (conn->marked_for_close)
continue;
if (conn->type == CONN_TYPE_CONTROL_LISTENER)
continue;
if (connection_is_listener(conn))
connection_mark_for_close(conn);
} SMARTLIST_FOREACH_END(conn);
}
/** DOCDOC */
void
connection_mark_all_noncontrol_connections(void)
{
SMARTLIST_FOREACH_BEGIN(get_connection_array(), connection_t *, conn) {
if (conn->marked_for_close)
continue;
switch (conn->type) {
case CONN_TYPE_CPUWORKER:
case CONN_TYPE_CONTROL_LISTENER:
case CONN_TYPE_CONTROL:
break;
case CONN_TYPE_AP:
connection_mark_unattached_ap(TO_ENTRY_CONN(conn),
END_STREAM_REASON_HIBERNATING);
break;
default:
connection_mark_for_close(conn);
break;
}
} SMARTLIST_FOREACH_END(conn);
}
/** Return 1 if we should apply rate limiting to <b>conn</b>, and 0 /** Return 1 if we should apply rate limiting to <b>conn</b>, and 0
* otherwise. * otherwise.
* Right now this just checks if it's an internal IP address or an * Right now this just checks if it's an internal IP address or an

View File

@ -66,6 +66,9 @@ int get_proxy_addrport(tor_addr_t *addr, uint16_t *port, int *proxy_type,
int retry_all_listeners(smartlist_t *replaced_conns, int retry_all_listeners(smartlist_t *replaced_conns,
smartlist_t *new_conns); smartlist_t *new_conns);
void connection_mark_all_noncontrol_listeners(void);
void connection_mark_all_noncontrol_connections(void);
ssize_t connection_bucket_write_limit(connection_t *conn, time_t now); ssize_t connection_bucket_write_limit(connection_t *conn, time_t now);
int global_write_bucket_low(connection_t *conn, size_t attempt, int priority); int global_write_bucket_low(connection_t *conn, size_t attempt, int priority);
void connection_bucket_init(void); void connection_bucket_init(void);

View File

@ -735,7 +735,6 @@ hibernate_soft_limit_reached(void)
static void static void
hibernate_begin(hibernate_state_t new_state, time_t now) hibernate_begin(hibernate_state_t new_state, time_t now)
{ {
connection_t *conn;
const or_options_t *options = get_options(); const or_options_t *options = get_options();
if (new_state == HIBERNATE_STATE_EXITING && if (new_state == HIBERNATE_STATE_EXITING &&
@ -756,15 +755,7 @@ hibernate_begin(hibernate_state_t new_state, time_t now)
} }
/* close listeners. leave control listener(s). */ /* close listeners. leave control listener(s). */
while ((conn = connection_get_by_type(CONN_TYPE_OR_LISTENER)) || connection_mark_all_noncontrol_listeners();
(conn = connection_get_by_type(CONN_TYPE_AP_LISTENER)) ||
(conn = connection_get_by_type(CONN_TYPE_AP_TRANS_LISTENER)) ||
(conn = connection_get_by_type(CONN_TYPE_AP_DNS_LISTENER)) ||
(conn = connection_get_by_type(CONN_TYPE_AP_NATD_LISTENER)) ||
(conn = connection_get_by_type(CONN_TYPE_DIR_LISTENER))) {
log_info(LD_NET,"Closing listener type %d", conn->type);
connection_mark_for_close(conn);
}
/* XXX kill intro point circs */ /* XXX kill intro point circs */
/* XXX upload rendezvous service descriptors with no intro points */ /* XXX upload rendezvous service descriptors with no intro points */

View File

@ -934,7 +934,7 @@ directory_info_has_arrived(time_t now, int from_cache)
update_extrainfo_downloads(now); update_extrainfo_downloads(now);
} }
if (server_mode(options) && !we_are_hibernating() && !from_cache && if (server_mode(options) && !net_is_disabled() && !from_cache &&
(can_complete_circuit || !any_predicted_circuits(now))) (can_complete_circuit || !any_predicted_circuits(now)))
consider_testing_reachability(1, 1); consider_testing_reachability(1, 1);
} }
@ -1161,11 +1161,11 @@ run_scheduled_events(time_t now)
if (router_rebuild_descriptor(1)<0) { if (router_rebuild_descriptor(1)<0) {
log_info(LD_CONFIG, "Couldn't rebuild router descriptor"); log_info(LD_CONFIG, "Couldn't rebuild router descriptor");
} }
if (advertised_server_mode()) if (advertised_server_mode() & !options->DisableNetwork)
router_upload_dir_desc_to_dirservers(0); router_upload_dir_desc_to_dirservers(0);
} }
if (time_to_try_getting_descriptors < now) { if (!options->DisableNetwork && time_to_try_getting_descriptors < now) {
update_all_descriptor_downloads(now); update_all_descriptor_downloads(now);
update_extrainfo_downloads(now); update_extrainfo_downloads(now);
if (router_have_minimum_dir_info()) if (router_have_minimum_dir_info())
@ -1219,7 +1219,7 @@ run_scheduled_events(time_t now)
if (time_to_launch_reachability_tests < now && if (time_to_launch_reachability_tests < now &&
(authdir_mode_tests_reachability(options)) && (authdir_mode_tests_reachability(options)) &&
!we_are_hibernating()) { !net_is_disabled()) {
time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL; time_to_launch_reachability_tests = now + REACHABILITY_TEST_INTERVAL;
/* try to determine reachability of the other Tor relays */ /* try to determine reachability of the other Tor relays */
dirserv_test_reachability(now); dirserv_test_reachability(now);
@ -1355,7 +1355,7 @@ run_scheduled_events(time_t now)
/* 2b. Once per minute, regenerate and upload the descriptor if the old /* 2b. Once per minute, regenerate and upload the descriptor if the old
* one is inaccurate. */ * one is inaccurate. */
if (time_to_check_descriptor < now) { if (time_to_check_descriptor < now && !options->DisableNetwork) {
static int dirport_reachability_count = 0; static int dirport_reachability_count = 0;
time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL; time_to_check_descriptor = now + CHECK_DESCRIPTOR_INTERVAL;
check_descriptor_bandwidth_changed(now); check_descriptor_bandwidth_changed(now);
@ -1430,7 +1430,7 @@ run_scheduled_events(time_t now)
connection_expire_held_open(); connection_expire_held_open();
/** 3d. And every 60 seconds, we relaunch listeners if any died. */ /** 3d. And every 60 seconds, we relaunch listeners if any died. */
if (!we_are_hibernating() && time_to_check_listeners < now) { if (!net_is_disabled() && time_to_check_listeners < now) {
retry_all_listeners(NULL, NULL); retry_all_listeners(NULL, NULL);
time_to_check_listeners = now+60; time_to_check_listeners = now+60;
} }
@ -1441,7 +1441,7 @@ run_scheduled_events(time_t now)
* and we make a new circ if there are no clean circuits. * and we make a new circ if there are no clean circuits.
*/ */
have_dir_info = router_have_minimum_dir_info(); have_dir_info = router_have_minimum_dir_info();
if (have_dir_info && !we_are_hibernating()) if (have_dir_info && !net_is_disabled())
circuit_build_needed_circs(now); circuit_build_needed_circs(now);
/* every 10 seconds, but not at the same second as other such events */ /* every 10 seconds, but not at the same second as other such events */
@ -1472,7 +1472,7 @@ run_scheduled_events(time_t now)
circuit_close_all_marked(); circuit_close_all_marked();
/** 7. And upload service descriptors if necessary. */ /** 7. And upload service descriptors if necessary. */
if (can_complete_circuit && !we_are_hibernating()) { if (can_complete_circuit && !net_is_disabled()) {
rend_consider_services_upload(now); rend_consider_services_upload(now);
rend_consider_descriptor_republication(); rend_consider_descriptor_republication();
} }
@ -1489,7 +1489,8 @@ run_scheduled_events(time_t now)
/** 9. and if we're a server, check whether our DNS is telling stories to /** 9. and if we're a server, check whether our DNS is telling stories to
* us. */ * us. */
if (public_server_mode(options) && time_to_check_for_correct_dns < now) { if (!net_is_disabled() &&
public_server_mode(options) && time_to_check_for_correct_dns < now) {
if (!time_to_check_for_correct_dns) { if (!time_to_check_for_correct_dns) {
time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120); time_to_check_for_correct_dns = now + 60 + crypto_rand_int(120);
} else { } else {
@ -1508,7 +1509,8 @@ run_scheduled_events(time_t now)
} }
/** 11. check the port forwarding app */ /** 11. check the port forwarding app */
if (time_to_check_port_forwarding < now && if (!net_is_disabled() &&
time_to_check_port_forwarding < now &&
options->PortForwarding && options->PortForwarding &&
is_server) { is_server) {
#define PORT_FORWARDING_CHECK_INTERVAL 5 #define PORT_FORWARDING_CHECK_INTERVAL 5
@ -1520,7 +1522,7 @@ run_scheduled_events(time_t now)
} }
/** 11b. check pending unconfigured managed proxies */ /** 11b. check pending unconfigured managed proxies */
if (pt_proxies_configuration_pending()) if (!net_is_disabled() && pt_proxies_configuration_pending())
pt_configure_remaining_proxies(); pt_configure_remaining_proxies();
/** 11c. validate pluggable transports configuration if we need to */ /** 11c. validate pluggable transports configuration if we need to */
@ -1592,7 +1594,7 @@ second_elapsed_callback(periodic_timer_t *timer, void *arg)
control_event_stream_bandwidth_used(); control_event_stream_bandwidth_used();
if (server_mode(options) && if (server_mode(options) &&
!we_are_hibernating() && !net_is_disabled() &&
seconds_elapsed > 0 && seconds_elapsed > 0 &&
can_complete_circuit && can_complete_circuit &&
stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT != stats_n_seconds_working / TIMEOUT_UNTIL_UNREACHABILITY_COMPLAINT !=
@ -1793,6 +1795,7 @@ do_hup(void)
/* retry appropriate downloads */ /* retry appropriate downloads */
router_reset_status_download_failures(); router_reset_status_download_failures();
router_reset_descriptor_download_failures(); router_reset_descriptor_download_failures();
if (!options->DisableNetwork)
update_networkstatus_downloads(time(NULL)); update_networkstatus_downloads(time(NULL));
/* We'll retry routerstatus downloads in about 10 seconds; no need to /* We'll retry routerstatus downloads in about 10 seconds; no need to

View File

@ -3439,6 +3439,10 @@ typedef struct {
* issue. */ * issue. */
int UserspaceIOCPBuffers; int UserspaceIOCPBuffers;
/** If 1, we accept and launch no external network connections, except on
* control ports. */
int DisableNetwork;
} or_options_t; } or_options_t;
/** Persistent state for an onion router, as saved to disk. */ /** Persistent state for an onion router, as saved to disk. */

View File

@ -780,7 +780,7 @@ check_whether_dirport_reachable(void)
const or_options_t *options = get_options(); const or_options_t *options = get_options();
return !options->DirPort || return !options->DirPort ||
options->AssumeReachable || options->AssumeReachable ||
we_are_hibernating() || net_is_disabled() ||
can_reach_dir_port; can_reach_dir_port;
} }
@ -806,7 +806,7 @@ decide_to_advertise_dirport(const or_options_t *options, uint16_t dir_port)
return 0; return 0;
if (authdir_mode(options)) /* always publish */ if (authdir_mode(options)) /* always publish */
return dir_port; return dir_port;
if (we_are_hibernating()) if (net_is_disabled())
return 0; return 0;
if (!check_whether_dirport_reachable()) if (!check_whether_dirport_reachable())
return 0; return 0;
@ -974,6 +974,14 @@ router_perform_bandwidth_test(int num_circs, time_t now)
} }
} }
/** Return true iff our network is in some sense disabled: either we're
* hibernating, entering hibernation, or */
int
net_is_disabled(void)
{
return get_options()->DisableNetwork || we_are_hibernating();
}
/** Return true iff we believe ourselves to be an authoritative /** Return true iff we believe ourselves to be an authoritative
* directory server. * directory server.
*/ */

View File

@ -39,6 +39,8 @@ void router_orport_found_reachable(void);
void router_dirport_found_reachable(void); void router_dirport_found_reachable(void);
void router_perform_bandwidth_test(int num_circs, time_t now); void router_perform_bandwidth_test(int num_circs, time_t now);
int net_is_disabled(void);
int authdir_mode(const or_options_t *options); int authdir_mode(const or_options_t *options);
int authdir_mode_v1(const or_options_t *options); int authdir_mode_v1(const or_options_t *options);
int authdir_mode_v2(const or_options_t *options); int authdir_mode_v2(const or_options_t *options);

View File

@ -3244,7 +3244,7 @@ router_set_status(const char *digest, int up)
log_debug(LD_DIR,"Marking router %s as %s.", log_debug(LD_DIR,"Marking router %s as %s.",
node_describe(node), up ? "up" : "down"); node_describe(node), up ? "up" : "down");
#endif #endif
if (!up && node_is_me(node) && !we_are_hibernating()) if (!up && node_is_me(node) && !net_is_disabled())
log_warn(LD_NET, "We just marked ourself as down. Are your external " log_warn(LD_NET, "We just marked ourself as down. Are your external "
"addresses reachable?"); "addresses reachable?");
node->is_running = up; node->is_running = up;
@ -4009,6 +4009,8 @@ signed_desc_digest_is_recognized(signed_descriptor_t *desc)
void void
update_all_descriptor_downloads(time_t now) update_all_descriptor_downloads(time_t now)
{ {
if (get_options()->DisableNetwork)
return;
update_router_descriptor_downloads(now); update_router_descriptor_downloads(now);
update_microdesc_downloads(now); update_microdesc_downloads(now);
launch_dummy_descriptor_download_as_needed(now, get_options()); launch_dummy_descriptor_download_as_needed(now, get_options());
@ -4021,6 +4023,8 @@ routerlist_retry_directory_downloads(time_t now)
{ {
router_reset_status_download_failures(); router_reset_status_download_failures();
router_reset_descriptor_download_failures(); router_reset_descriptor_download_failures();
if (get_options()->DisableNetwork)
return;
update_networkstatus_downloads(now); update_networkstatus_downloads(now);
update_all_descriptor_downloads(now); update_all_descriptor_downloads(now);
} }