commit 8f313c620eeec6bfa3c5845fb5c3a7ff187f82ea Author: Damian Johnson atagar@torproject.org Date: Wed Jul 27 07:44:34 2011 -0700
Added validation that torrc is wizard generated
Doing regex checks that the options match what the arm wizard would produce. --- src/resources/torrcOverride/override.py | 92 ++++++++++++++++++++++++++++--- 1 files changed, 83 insertions(+), 9 deletions(-)
diff --git a/src/resources/torrcOverride/override.py b/src/resources/torrcOverride/override.py index 21e6b28..dffa3a5 100755 --- a/src/resources/torrcOverride/override.py +++ b/src/resources/torrcOverride/override.py @@ -29,6 +29,7 @@ pkill -sighup tor """
import os +import re import sys import grp import pwd @@ -41,13 +42,40 @@ USER = "tor-arm" GROUP = "tor-arm" TOR_CONFIG_FILE = "/etc/tor/torrc" ARM_CONFIG_FILE = "/var/lib/tor-arm/torrc" +RUN_VERIFY = True # use 'tor --verify-config' to check the torrc + +# regex patterns for options the wizard may include +WIZARD_OPT = ("^DataDirectory \S+$", + "^Log notice file \S+$", + "^ControlPort 9052$", + "^CookieAuthentication 1$", + "^RunAsDaemon 1$", + "^User \S+$", + "^ORPort (443|9001)$", + "^DirPort (80|9030)$", + "^Nickname ", + "^ContactInfo ", + "^BridgeRelay 1$", + "^PublishServerDescriptor 0$", + "^RelayBandwidthRate ", + "^RelayBandwidthBurst ", + "^AccountingMax ", + "^SocksPort 0$", + "^PortForwarding 1$", + "^ExitPolicy (accept|reject) \S+$", + "^DirPortFrontPage \S+$", + "^ClientOnly 1$", + "^MaxCircuitDirtiness ", + "^UseBridges 1$", + "^Bridge ")
HELP_MSG = """Usage %s [OPTION] Backup the system wide torrc (%s) and replace it with the contents of %s.
- --init creates the necessary user and paths - --remove reverts changes made with --init + --init creates the necessary user and paths + --remove reverts changes made with --init + --validate PATH checks if the given file is wizard generated """ % (os.path.basename(sys.argv[0]), TOR_CONFIG_FILE, ARM_CONFIG_FILE)
def init(): @@ -196,27 +224,37 @@ def replaceTorrc(): # man, unix is really weird... _, status = os.waitpid(fork_pid, 0)
- if status != 0: + if status != 0 or not os.path.exists(tf.name): print "The child seems to have failed; exiting!" tf.close() sys.exit(1)
# attempt to verify that the config is OK - if os.path.exists(tf.name): - # construct our SU string - SUSTRING = "su -c 'tor --verify-config -f " + str(tf.name) + "' " + USER - # We raise privs to drop them with 'su' + if RUN_VERIFY: + # raise privilages to drop them with 'su' os.setuid(0) os.seteuid(0) os.setgid(0) os.setegid(0) - # We drop privs here and exec tor to verify it as the dropped_uid + + # drop privilages and exec tor to verify it as the dropped_uid print "Using Tor to verify that arm will not break Tor's config:" - success = os.system(SUSTRING) + verifyCmd = "su -c 'tor --verify-config -f %s' %s" % (tf.name, USER) + success = os.system(verifyCmd) + if success != 0: print "Tor says the new configuration file is invalid: %s (%s)" % (ARM_CONFIG_FILE, tf.name) sys.exit(1)
+ # validates that the torrc matches what the wizard could produce + torrcFile = open(tf.name) + torrcContents = torrcFile.readlines() + torrcFile.close() + + if not isWizardGenerated(torrcContents): + print "torrc doesn't match what we'd expect from the setup wizard" + sys.exit(1) + # backup the previous tor config if os.path.exists(TOR_CONFIG_FILE): try: @@ -243,6 +281,34 @@ def replaceTorrc():
sys.exit(0)
+def isWizardGenerated(torrcLines): + """ + True if the given torrc contents could be generated by the wizard, false + otherwise. This just checks the basic format and options used, not the + validity of most generated values so this could still be rejected by tor. + """ + + wizardPatterns = [re.compile(opt) for opt in WIZARD_OPT] + + for line in torrcLines: + commentStart = line.find("#") + if commentStart != -1: line = line[:commentStart] + + line = line.strip() + if not line: continue # blank line + + isRecognized = False + for pattern in wizardPatterns: + if pattern.match(line): + isRecognized = True + break + + if not isRecognized: + print "Unrecognized torrc contents: %s" % line + return False + + return True + if __name__ == "__main__": # sanity check that we're on linux if os.name != "posix": @@ -260,6 +326,14 @@ if __name__ == "__main__": init() elif len(sys.argv) == 2 and sys.argv[1] == "--remove": remove() + elif len(sys.argv) == 3 and sys.argv[1] == "--validate": + torrcFile = open(sys.argv[2]) + torrcContents = torrcFile.readlines() + torrcFile.close() + + isValid = isWizardGenerated(torrcContents) + if isValid: print "torrc validated" + else: print "torrc invalid" else: print HELP_MSG sys.exit(1)
tor-commits@lists.torproject.org