--- a/hg-ssh Mon Apr 21 12:37:56 2008 +0100
+++ b/hg-ssh Tue Apr 22 09:46:29 2008 +0100
@@ -2,7 +2,7 @@
#
# Copyright 2008 LShift Ltd
# Copyright 2005-2007 by Intevation GmbH <intevation@intevation.de>
-# Author(s):
+# Authors:
# Paul Crowley <paul@lshift.net>
# Thomas Arendsen Hein <thomas@intevation.de>
# with ideas from Mathieu PASQUET <kiorky@cryptelium.net>
@@ -14,15 +14,18 @@
hg-ssh - limit access to hg repositories reached via ssh. Part of
hg-admin-tools.
-This script is called by hg-ssh-wrapper with two arguments:
-
-hg-ssh <rulefile> <keyname>
+This script is called by hg-ssh-wrapper with no arguments - everything
+should be in enviroment variables:
-It expects to find the command the SSH user was trying to run in the
-environment variable SSH_ORIGINAL_COMMAND, and uses it to determine
-what the user was trying to do and to what repository, and then checks
-each rule in the rule file in turn for a matching rule which decides
-what to do, defaulting to disallowing the action.
+HG_ACCESS_RULES_FILE identifies the path to the rules file
+REMOTE_USER the remote user (which is the key used by ssh)
+SSH_ORIGINAL_COMMAND the command the user was trying to run
+
+It uses SSH_ORIGINAL_COMMAND to determine what the user was trying to
+do and to what repository, and then checks each rule in the rule file
+in turn for a matching rule which decides what to do, defaulting to
+disallowing the action.
+
"""
# enable importing on demand to reduce startup time
@@ -30,70 +33,44 @@
from mercurial import dispatch
-import sys, os, re
+import sys, os
+import rules
def fail(message):
#logfile.write("Fail: %s\n" % message)
sys.stderr.write(message + "\n")
sys.exit(-1)
-# Note that this currently disallows dots in path components - if you change it
-# to allow them ensure that "." and ".." are disallowed in path components.
-allowedchars = "A-Za-z0-9_-"
-goodpathre = re.compile("([%s]+/)*[%s]+$" % (allowedchars, allowedchars))
-def goodpath(path):
- if goodpathre.match(path) is None:
+def getpath(path):
+ if path.endswith("/"):
+ path = path[:-1]
+ if not rules.goodpath(path):
fail("Disallowing path: %s" % path)
-
-# Don't put anything except *A-Za-z0-9_- in rule globs or
-# you'll probably break security. No regexp metachars, not even .
-# We may fix this later.
-goodglobre = re.compile("[*/%s]+$" % allowedchars)
-def globmatch(pattern, match):
- if goodglobre.match(pattern) is None:
- fail("Bad glob pattern in auth config: %s" % pattern)
- pattern = pattern.replace(".", r'\.')
- pattern = pattern.replace("*", "[%s]*" % allowedchars)
- return re.compile(pattern + "$").match(match) is not None
+ return path
-def testrule(rulefile, keyname, path, applicable):
- goodpath(keyname)
- goodpath(path)
- f = open(rulefile)
- try:
- for l in f:
- l = l.strip()
- if l == "" or l.startswith("#"):
- continue
- rule, rk, rp = l.split()
- if globmatch(rk, keyname) and globmatch(rp, path):
- #logfile.write("Used rule: %s\n" % l)
- return rule in applicable
- finally:
- f.close()
- return False
-
-def get_cmd(rulefile, keyname, cmd):
+def get_cmd(rules, remoteuser, cmd):
if cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'):
- path = cmd[6:-14]
- if testrule(rulefile, keyname, path, set(["allow", "init"])):
- return ['-R', path, 'serve', '--stdio']
+ repo = getpath(cmd[6:-14])
+ if rules.allow("read", user=remoteuser, repo=repo, file=None):
+ os.environ["HG_REPO_PATH"] = repo
+ return ['-R', repo, 'serve', '--stdio']
elif cmd.startswith('hg init '):
- path = cmd[8:]
- if testrule(rulefile, keyname, path, set(["init"])):
- return ['init', path]
+ repo = getpath(cmd[8:])
+ if rules.allow("init", user=remoteuser, repo=repo, file=None):
+ os.environ["HG_REPO_PATH"] = repo
+ return ['init', repo]
fail("Illegal command %r" % cmd)
#logfile = open("/tmp/hg-ssh.%d.txt" % os.getpid(), "w")
#logfile.write("Started: %s\n" % sys.argv)
-if len(sys.argv) != 3:
- fail("hg-ssh must have exactly two arguments (%s)"
+if len(sys.argv) != 1:
+ fail("hg-ssh must have no arguments (%s)"
% sys.argv)
-rulefile = sys.argv[1]
-keyname = sys.argv[2]
-todispatch = get_cmd(rulefile, keyname,
+rules = rules.Ruleset.readfile(os.environ['HG_ACCESS_RULES_FILE'])
+remoteuser = getpath(os.environ['REMOTE_USER'])
+todispatch = get_cmd(rules, remoteuser,
os.environ.get('SSH_ORIGINAL_COMMAND', '?'))
dispatch.dispatch(todispatch)