# HG changeset patch # User Paul Crowley # Date 1261250501 0 # Node ID 107d28ce67f5205c2032983b63457c06c06bd204 # Parent 97a8fe72a35e0de568c7beecee47c8f57d9a09a8# Parent efb4044593ed795d8e74cd38c216998c317eccfa Merge in changes from upstream diff -r 97a8fe72a35e -r 107d28ce67f5 .hgtags --- a/.hgtags Fri Dec 18 13:25:45 2009 +0000 +++ b/.hgtags Sat Dec 19 19:21:41 2009 +0000 @@ -6,4 +6,6 @@ 1e4050abb96e72c6324b93709e56a3e135e63ce1 debian_0.8-1 d42d3f5311c55cab668d6962a61d44ba98645e release_0.9 b6887a9b8792bb2b69b428448102140fce121e29 debian_0.9-1 +8ce190faa5c2b50f63cc5b11e28daf98836498d8 release_1.0 60c2d676a754e02f1f7656a4b137399438b2ed35 debian_1.0-1 +92cb6640a6417edaf52870c8a97000e11bb8b138 release_1.0.1 diff -r 97a8fe72a35e -r 107d28ce67f5 CREDITS --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CREDITS Sat Dec 19 19:21:41 2009 +0000 @@ -0,0 +1,22 @@ +mercurial-server is by Paul Crowley + +Thanks to: + +Thomas Arendsen Hein +Mathieu PASQUET +Vadim Gelfer +Hubert Plociniczak +Christoph Junghans +Steve Kemp +Cédric Boutillier +Justin B Rye +Wolfgang Karall +Helge Kreutzmann +"Hideki Yamane \(Debian-JP\)" +Michal Simunek +Martin Bagge +Vincenzo Campanella +Ji ZhengYu + +This credits file may be incomplete - please remind me about people I +should add! diff -r 97a8fe72a35e -r 107d28ce67f5 NEWS --- a/NEWS Fri Dec 18 13:25:45 2009 +0000 +++ b/NEWS Sat Dec 19 19:21:41 2009 +0000 @@ -1,3 +1,17 @@ +====================== +mercurial-server 1.0.1 +====================== + +* Fix HGRCPATH brokenness - potential security issue +* Fix rule matching to properly handle the case where we don't know for sure +* Fix error in documentation +* Remove whitespace around paths, said to help with TortoiseHG +* Small refactor of access.py +* Tidy up file prologues; move credits to CREDITS + +Upgrading: repositories whose paths begin or end in white space will no longer +be accessible; if they exist they must be moved to new names. + ==================== mercurial-server 1.0 ==================== diff -r 97a8fe72a35e -r 107d28ce67f5 doc/manual.docbook --- a/doc/manual.docbook Fri Dec 18 13:25:45 2009 +0000 +++ b/doc/manual.docbook Sat Dec 19 19:21:41 2009 +0000 @@ -147,7 +147,7 @@ class='directory'>keys/users. To grant this key access, we must give mercurial-server a new access rule, so we create a file in hgadmin called access.conf, with the following contents: # Give Pat access to the "widget" repository -write repo=widget user=pat +write repo=widget user=pat/* Pat will have read and write access to the -# Authors: -# Paul Crowley -# Thomas Arendsen Hein -# with ideas from Mathieu PASQUET -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. """ hg-ssh - limit access to hg repositories reached via ssh. Part of @@ -55,7 +45,7 @@ def getrepo(op, repo): # First canonicalise, then check the string, then the rules # and finally the filesystem. - repo = repo.rstrip("/") + repo = repo.strip().rstrip("/") if len(repo) == 0: fail("path to repository seems to be empty") if repo.startswith("/"): @@ -69,6 +59,9 @@ config.initExe() +for k,v in config.getEnv(): + os.environ[k.upper()] = v + if len(sys.argv) == 3 and sys.argv[1] == "--base64": ruleset.rules.set(user = base64.b64decode(sys.argv[2])) elif len(sys.argv) == 2: @@ -77,9 +70,6 @@ fail("hg-ssh wrongly called, is authorized_keys corrupt? (%s)" % sys.argv) -for k,v in config.getEnv(): - os.environ[k] = v - os.chdir(config.getReposPath()) for f in config.getAccessPaths(): diff -r 97a8fe72a35e -r 107d28ce67f5 src/mercurialserver/access.py --- a/src/mercurialserver/access.py Fri Dec 18 13:25:45 2009 +0000 +++ b/src/mercurialserver/access.py Sat Dec 19 19:21:41 2009 +0000 @@ -1,12 +1,4 @@ -# Copyright 2008-2009 LShift Ltd -# Copyright 2006 Vadim Gelfer -# -# Authors: -# Paul Crowley -# Vadim Gelfer -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. +"""Mercurial access control hook""" from mercurial.i18n import _ import mercurial.util @@ -16,32 +8,21 @@ from mercurialserver import ruleset from mercurialserver import changes -class Checker(object): - '''acl checker.''' - - def __init__(self, ui, repo): - self.ui = ui - self.repo = repo - - def allow(self, ctx): - branch = ctx.branch() - if not ruleset.rules.allow("write", branch=branch, file=None): +def allow(ctx): + branch = ctx.branch() + if not ruleset.rules.allow("write", branch=branch, file=None): + return False + for f in ctx.files(): + if not ruleset.rules.allow("write", branch=branch, file=f): return False - for f in ctx.files(): - if not ruleset.rules.allow("write", branch=branch, file=f): - return False - return True - - def check(self, ctx): - '''return if access allowed, raise exception if not.''' - if not self.allow(ctx): - raise mercurial.util.Abort(_('%s: access denied for changeset %s') % - (__name__, mercurial.node.short(ctx.node()))) + return True def hook(ui, repo, hooktype, node=None, source=None, **kwargs): if hooktype != 'pretxnchangegroup': raise mercurial.util.Abort(_('config error - hook type "%s" cannot stop ' 'incoming changesets') % hooktype) - c = Checker(ui, repo) for ctx in changes.changes(repo, node): - c.check(ctx) + if not allow(ctx): + raise mercurial.util.Abort(_('%s: access denied for changeset %s') % + (__name__, mercurial.node.short(ctx.node()))) + diff -r 97a8fe72a35e -r 107d28ce67f5 src/mercurialserver/changes.py --- a/src/mercurialserver/changes.py Fri Dec 18 13:25:45 2009 +0000 +++ b/src/mercurialserver/changes.py Sat Dec 19 19:21:41 2009 +0000 @@ -1,12 +1,6 @@ -# Copyright 2008-2009 LShift Ltd -# Copyright 2006 Vadim Gelfer -# -# Authors: -# Paul Crowley -# Vadim Gelfer -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. +""" +Find all the changes in a node in a way portable across Mercurial versions +""" def changes(repo, node): start = repo.changectx(node).rev() diff -r 97a8fe72a35e -r 107d28ce67f5 src/mercurialserver/config.py --- a/src/mercurialserver/config.py Fri Dec 18 13:25:45 2009 +0000 +++ b/src/mercurialserver/config.py Sat Dec 19 19:21:41 2009 +0000 @@ -1,4 +1,6 @@ -# Copyright 2008-2009 LShift Ltd +""" +Fix $HOME and read ~/.mercurial-server +""" import sys import os diff -r 97a8fe72a35e -r 107d28ce67f5 src/mercurialserver/refreshauth.py --- a/src/mercurialserver/refreshauth.py Fri Dec 18 13:25:45 2009 +0000 +++ b/src/mercurialserver/refreshauth.py Sat Dec 19 19:21:41 2009 +0000 @@ -1,9 +1,6 @@ -# Copyright 2008-2009 LShift Ltd - -# WARNING -# This hook completely destroys your ~/.ssh/authorized_keys -# file every time it is run -# WARNING +""" +Rewrite ~/.ssh/authorized_keys by recursing through key directories +""" import re import base64 diff -r 97a8fe72a35e -r 107d28ce67f5 src/mercurialserver/ruleset.py --- a/src/mercurialserver/ruleset.py Fri Dec 18 13:25:45 2009 +0000 +++ b/src/mercurialserver/ruleset.py Sat Dec 19 19:21:41 2009 +0000 @@ -1,29 +1,31 @@ -# Copyright 2008-2009 LShift Ltd -# Author(s): -# Paul Crowley -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. +""" +Glob-based, order-based rules matcher that can answer "maybe" +where the inputs make clear that something is unknown. +""" import sys import re import os import os.path -allowedchars = "A-Za-z0-9_-" - def globmatcher(pattern): p = "[^/]*".join(re.escape(c) for c in pattern.split("*")) # ** means "match recursively" ie "ignore directories" - rex = re.compile(p.replace("[^/]*[^/]*", ".*") + "$") - # None matches everything - return lambda x: x is None or rex.match(x) is not None + return re.compile(p.replace("[^/]*[^/]*", ".*") + "$") +# Returns True for a definite match +# False for a definite non-match +# None where we can't be sure because a key is None def rule(pairs): matchers = [(k, globmatcher(v)) for k, v in pairs] def c(kw): for k, m in matchers: - if k not in kw or not m(kw[k]): + if k not in kw: + return False + kkw = kw[k] + if kkw is None: + return None + if m.match(kkw) is None: return False return True return c @@ -46,32 +48,39 @@ def get(self, k): return self.preset.get(k, None) - def matchrule(self, kw): + def matchrules(self, kw): d = self.preset.copy() d.update(kw) + res = set() for a, c in self.rules: - if c(d): - return a - return None + m = c(d) + if m is None: + # "Maybe match" - add it and carry on + res.add(a) + elif m: + # Definite match - add it and stop + res.add(a) + break + return res def allow(self, level, **kw): - a = self.matchrule(kw) - return a in self.levels and self.levels.index(a) <= self.levels.index(level) + for a in self.matchrules(kw): + if a in self.levels: + if self.levels.index(a) <= self.levels.index(level): + return True + return False def readfile(self, fn): + f = open(fn) try: - f = open(fn) - try: - for l in f: - l = l.strip() - if len(l) == 0 or l.startswith("#"): - continue - l = l.split() - self.add(l[0], rule([c.split("=", 1) for c in l[1:]])) - finally: - f.close() - except Exception, e: - print >> sys.stderr, "Failure reading rules file:", e + for l in f: + l = l.strip() + if len(l) == 0 or l.startswith("#"): + continue + l = l.split() + self.add(l[0], rule([c.split("=", 1) for c in l[1:]])) + finally: + f.close() rules = Ruleset() diff -r 97a8fe72a35e -r 107d28ce67f5 src/mercurialserver/servelog.py --- a/src/mercurialserver/servelog.py Fri Dec 18 13:25:45 2009 +0000 +++ b/src/mercurialserver/servelog.py Sat Dec 19 19:21:41 2009 +0000 @@ -1,10 +1,6 @@ -# Copyright 2008-2009 LShift Ltd -# -# Authors: -# Paul Crowley -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. +""" +Hook to log changesets pushed and pulled +""" from mercurial.i18n import _ import mercurial.util diff -r 97a8fe72a35e -r 107d28ce67f5 src/refresh-auth --- a/src/refresh-auth Fri Dec 18 13:25:45 2009 +0000 +++ b/src/refresh-auth Sat Dec 19 19:21:41 2009 +0000 @@ -1,10 +1,7 @@ #!/usr/bin/env python -# Copyright 2008-2009 LShift Ltd - -# WARNING -# This script completely destroys your ~/.ssh/authorized_keys -# file every time it is run -# WARNING +""" +Rewrite ~/.ssh/authorized_keys by recursing through key directories +""" import sys import os