diff -r e99262dfa950 -r 731a72b742db src/hg-ssh --- a/src/hg-ssh Thu May 28 10:43:30 2009 +0100 +++ b/src/hg-ssh Tue Oct 13 15:30:03 2009 +0100 @@ -33,45 +33,53 @@ from mercurial import dispatch -import sys, os +import sys, os, os.path +import base64 from mercurialserver import ruleset, paths def fail(message): - #logfile.write("Fail: %s\n" % message) - sys.stderr.write(message + "\n") + sys.stderr.write("mercurial-server: %s\n" % message) sys.exit(-1) -def getpath(path): - if path.endswith("/"): - path = path[:-1] - if not ruleset.goodpath(path): - fail("Disallowing path: %s" % path) - return path +def checkDots(path): + head, tail = os.path.split(path) + if tail.startswith("."): + fail("paths cannot contain dot file components") + if head: + checkDots(head) + +def checkParents(path): + path = os.path.dirname(path) + if path == "": + return + if os.path.exists(path + "/.hg"): + fail("Cannot create repo under existing repo") + checkParents(path) -def try_cmd(cmd): - if cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'): - repo = getpath(cmd[6:-14]) - ruleset.rules.set(repo=repo) - if ruleset.rules.allow("read", branch=None, file=None): - dispatch.dispatch(['-R', repo, 'serve', '--stdio']) - return - elif cmd.startswith('hg init '): - repo = getpath(cmd[8:]) - ruleset.rules.set(repo=repo) - if ruleset.rules.allow("init", branch=None, file=None): - dispatch.dispatch(['init', repo]) - return - 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) != 2: - fail("hg-ssh must have exactly one argument (%s)" - % sys.argv) +def getrepo(op, repo): + # First canonicalise, then check the string, then the rules + # and finally the filesystem. + repo = repo.rstrip("/") + if len(repo) == 0: + fail("path to repository seems to be empty") + if repo.startswith("/"): + fail("absolute paths are not supported") + checkDots(path) + ruleset.rules.set(repo=repo) + if not ruleset.rules.allow(op, branch=None, file=None): + fail("access denied") + checkParents(repo) + return repo paths.setExePath() -ruleset.rules.set(user = sys.argv[1]) + +if len(sys.argv) == 3 and sys.argv[1] == "--base64": + ruleset.rules.set(user = base64.b64decode(sys.argv[2])) +elif len(sys.argv) == 2: + ruleset.rules.set(user = sys.argv[1]) +else: + fail("hg-ssh wrongly called, is authorized_keys corrupt? (%s)" + % sys.argv) # Use a different hgrc for remote pulls - this way you can set # up access.py for everything at once without affecting local operations @@ -86,4 +94,22 @@ if os.path.isfile(f): ruleset.rules.readfile(f) -try_cmd(os.environ.get('SSH_ORIGINAL_COMMAND', '?')) +cmd = os.environ.get('SSH_ORIGINAL_COMMAND', None) +if cmd is None: + fail("direct logins on the hg account prohibited") +elif cmd.startswith('hg -R ') and cmd.endswith(' serve --stdio'): + repo = getrepo("read", cmd[6:-14]) + if not os.path.isdir(repo + "/.hg"): + fail("no such repository %s" % repo) + dispatch.dispatch(['-R', repo, 'serve', '--stdio']) +elif cmd.startswith('hg init '): + repo = getrepo("init", cmd[8:]) + if os.path.exists(repo): + fail("%s exists" % repo) + d = os.path.dirname(repo) + if d != "" and not os.path.isdir(d): + os.makedirs(d) + dispatch.dispatch(['init', repo]) +else: + fail("illegal command %r" % cmd) +