##// END OF EJS Templates
Only read .hg/hgrc files from trusted users/groups...
Alexis S. L. Carvalho -
r3013:494521a3 default
parent child Browse files
Show More
@@ -0,0 +1,112 b''
1 #!/usr/bin/env python
2 # Since it's not easy to write a test that portably deals
3 # with files from different users/groups, we cheat a bit by
4 # monkey-patching some functions in the util module
5
6 import os
7 from mercurial import ui, util
8
9 hgrc = os.environ['HGRCPATH']
10
11 def testui(user='foo', group='bar', tusers=(), tgroups=(),
12 cuser='foo', cgroup='bar'):
13 # user, group => owners of the file
14 # tusers, tgroups => trusted users/groups
15 # cuser, cgroup => user/group of the current process
16
17 # write a global hgrc with the list of trusted users/groups and
18 # some setting so that we can be sure it was read
19 f = open(hgrc, 'w')
20 f.write('[paths]\n')
21 f.write('global = /some/path\n\n')
22
23 if tusers or tgroups:
24 f.write('[trusted]\n')
25 if tusers:
26 f.write('users = %s\n' % ', '.join(tusers))
27 if tgroups:
28 f.write('groups = %s\n' % ', '.join(tgroups))
29 f.close()
30
31 # override the functions that give names to uids and gids
32 def username(uid=None):
33 if uid is None:
34 return cuser
35 return user
36 util.username = username
37
38 def groupname(gid=None):
39 if gid is None:
40 return 'bar'
41 return group
42 util.groupname = groupname
43
44 # try to read everything
45 #print '# File belongs to user %s, group %s' % (user, group)
46 #print '# trusted users = %s; trusted groups = %s' % (tusers, tgroups)
47 kind = ('different', 'same')
48 who = ('', 'user', 'group', 'user and the group')
49 trusted = who[(user in tusers) + 2*(group in tgroups)]
50 if trusted:
51 trusted = ', but we trust the ' + trusted
52 print '# %s user, %s group%s' % (kind[user == cuser], kind[group == cgroup],
53 trusted)
54
55 parentui = ui.ui()
56 u = ui.ui(parentui=parentui)
57 u.readconfig('.hg/hgrc')
58 for name, path in u.configitems('paths'):
59 print name, '=', path
60 print
61
62 return u
63
64 os.mkdir('repo')
65 os.chdir('repo')
66 os.mkdir('.hg')
67 f = open('.hg/hgrc', 'w')
68 f.write('[paths]\n')
69 f.write('local = /another/path\n\n')
70 f.close()
71
72 #print '# Everything is run by user foo, group bar\n'
73
74 # same user, same group
75 testui()
76 # same user, different group
77 testui(group='def')
78 # different user, same group
79 testui(user='abc')
80 # ... but we trust the group
81 testui(user='abc', tgroups=['bar'])
82 # different user, different group
83 testui(user='abc', group='def')
84 # ... but we trust the user
85 testui(user='abc', group='def', tusers=['abc'])
86 # ... but we trust the group
87 testui(user='abc', group='def', tgroups=['def'])
88 # ... but we trust the user and the group
89 testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
90 # ... but we trust all users
91 print '# we trust all users'
92 testui(user='abc', group='def', tusers=['*'])
93 # ... but we trust all groups
94 print '# we trust all groups'
95 testui(user='abc', group='def', tgroups=['*'])
96 # ... but we trust the whole universe
97 print '# we trust all users and groups'
98 testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
99 # ... check that users and groups are in different namespaces
100 print "# we don't get confused by users and groups with the same name"
101 testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
102 # ... lists of user names work
103 print "# list of user names"
104 testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
105 tgroups=['bar', 'baz', 'qux'])
106 # ... lists of group names work
107 print "# list of group names"
108 testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
109 tgroups=['bar', 'def', 'baz', 'qux'])
110
111 print "# Can't figure out the name of the user running this process"
112 testui(user='abc', group='def', cuser=None)
@@ -0,0 +1,67 b''
1 # same user, same group
2 global = /some/path
3 local = /another/path
4
5 # same user, different group
6 global = /some/path
7 local = /another/path
8
9 # different user, same group
10 not reading file .hg/hgrc from untrusted user abc, group bar
11 global = /some/path
12
13 # different user, same group, but we trust the group
14 global = /some/path
15 local = /another/path
16
17 # different user, different group
18 not reading file .hg/hgrc from untrusted user abc, group def
19 global = /some/path
20
21 # different user, different group, but we trust the user
22 global = /some/path
23 local = /another/path
24
25 # different user, different group, but we trust the group
26 global = /some/path
27 local = /another/path
28
29 # different user, different group, but we trust the user and the group
30 global = /some/path
31 local = /another/path
32
33 # we trust all users
34 # different user, different group
35 global = /some/path
36 local = /another/path
37
38 # we trust all groups
39 # different user, different group
40 global = /some/path
41 local = /another/path
42
43 # we trust all users and groups
44 # different user, different group
45 global = /some/path
46 local = /another/path
47
48 # we don't get confused by users and groups with the same name
49 # different user, different group
50 not reading file .hg/hgrc from untrusted user abc, group def
51 global = /some/path
52
53 # list of user names
54 # different user, different group, but we trust the user
55 global = /some/path
56 local = /another/path
57
58 # list of group names
59 # different user, different group, but we trust the group
60 global = /some/path
61 local = /another/path
62
63 # Can't figure out the name of the user running this process
64 # different user, different group
65 global = /some/path
66 local = /another/path
67
@@ -50,6 +50,8 b' installed.'
50 particular repository. This file is not version-controlled, and
50 particular repository. This file is not version-controlled, and
51 will not get transferred during a "clone" operation. Options in
51 will not get transferred during a "clone" operation. Options in
52 this file override options in all other configuration files.
52 this file override options in all other configuration files.
53 On Unix, this file is only read if it belongs to a trusted user
54 or to a trusted group.
53
55
54 SYNTAX
56 SYNTAX
55 ------
57 ------
@@ -349,6 +351,16 b' server::'
349 6Mbps), uncompressed streaming is slower, because of the extra
351 6Mbps), uncompressed streaming is slower, because of the extra
350 data transfer overhead. Default is False.
352 data transfer overhead. Default is False.
351
353
354 trusted::
355 Mercurial will only read the .hg/hgrc file from a repository if
356 it belongs to a trusted user or to a trusted group. This section
357 specifies what users and groups are trusted. To trust everybody,
358 list a user or a group with name "*".
359 users;;
360 Comma-separated list of trusted users.
361 groups;;
362 Comma-separated list of trusted groups.
363
352 ui::
364 ui::
353 User interface controls.
365 User interface controls.
354 debug;;
366 debug;;
@@ -19,6 +19,8 b' class ui(object):'
19 # this is the parent of all ui children
19 # this is the parent of all ui children
20 self.parentui = None
20 self.parentui = None
21 self.readhooks = list(readhooks)
21 self.readhooks = list(readhooks)
22 self.trusted_users = {}
23 self.trusted_groups = {}
22 self.cdata = ConfigParser.SafeConfigParser()
24 self.cdata = ConfigParser.SafeConfigParser()
23 self.readconfig(util.rcpath())
25 self.readconfig(util.rcpath())
24
26
@@ -37,6 +39,8 b' class ui(object):'
37 # parentui may point to an ui object which is already a child
39 # parentui may point to an ui object which is already a child
38 self.parentui = parentui.parentui or parentui
40 self.parentui = parentui.parentui or parentui
39 self.readhooks = list(parentui.readhooks or readhooks)
41 self.readhooks = list(parentui.readhooks or readhooks)
42 self.trusted_users = parentui.trusted_users.copy()
43 self.trusted_groups = parentui.trusted_groups.copy()
40 parent_cdata = self.parentui.cdata
44 parent_cdata = self.parentui.cdata
41 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
45 self.cdata = ConfigParser.SafeConfigParser(parent_cdata.defaults())
42 # make interpolation work
46 # make interpolation work
@@ -72,7 +76,22 b' class ui(object):'
72 fn = [fn]
76 fn = [fn]
73 for f in fn:
77 for f in fn:
74 try:
78 try:
75 self.cdata.read(f)
79 fp = open(f)
80 except IOError:
81 continue
82 if ((self.trusted_users or self.trusted_groups) and
83 '*' not in self.trusted_users and
84 '*' not in self.trusted_groups):
85 st = util.fstat(fp)
86 user = util.username(st.st_uid)
87 group = util.groupname(st.st_gid)
88 if (user not in self.trusted_users and
89 group not in self.trusted_groups):
90 self.warn(_('not reading file %s from untrusted '
91 'user %s, group %s\n') % (f, user, group))
92 continue
93 try:
94 self.cdata.readfp(fp, f)
76 except ConfigParser.ParsingError, inst:
95 except ConfigParser.ParsingError, inst:
77 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
96 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
78 # translate paths relative to root (or home) into absolute paths
97 # translate paths relative to root (or home) into absolute paths
@@ -81,6 +100,13 b' class ui(object):'
81 for name, path in self.configitems("paths"):
100 for name, path in self.configitems("paths"):
82 if path and "://" not in path and not os.path.isabs(path):
101 if path and "://" not in path and not os.path.isabs(path):
83 self.cdata.set("paths", name, os.path.join(root, path))
102 self.cdata.set("paths", name, os.path.join(root, path))
103 user = util.username()
104 if user is not None:
105 self.trusted_users[user] = 1
106 for user in self.configlist('trusted', 'users'):
107 self.trusted_users[user] = 1
108 for group in self.configlist('trusted', 'groups'):
109 self.trusted_groups[group] = 1
84 for hook in self.readhooks:
110 for hook in self.readhooks:
85 hook(self)
111 hook(self)
86
112
@@ -15,7 +15,7 b' platform-specific details from the core.'
15 from i18n import gettext as _
15 from i18n import gettext as _
16 from demandload import *
16 from demandload import *
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
17 demandload(globals(), "cStringIO errno getpass popen2 re shutil sys tempfile")
18 demandload(globals(), "os threading time")
18 demandload(globals(), "os threading time pwd grp")
19
19
20 # used by parsedate
20 # used by parsedate
21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
21 defaultdateformats = ('%Y-%m-%d %H:%M:%S', '%Y-%m-%d %H:%M',
@@ -509,6 +509,38 b' def getuser():'
509 raise Abort(_('user name not available - set USERNAME '
509 raise Abort(_('user name not available - set USERNAME '
510 'environment variable'))
510 'environment variable'))
511
511
512 def username(uid=None):
513 """Return the name of the user with the given uid.
514
515 If uid is None, return the name of the current user."""
516 try:
517 # force an ImportError if there's no module pwd
518 getpwuid = pwd.getpwuid
519 if uid is None:
520 uid = os.getuid()
521 try:
522 return getpwuid(uid)[0]
523 except KeyError:
524 return str(uid)
525 except ImportError:
526 return None
527
528 def groupname(gid=None):
529 """Return the name of the group with the given gid.
530
531 If gid is None, return the name of the current group."""
532 try:
533 # force an ImportError if there's no module grp
534 getgrgid = grp.getgrgid
535 if gid is None:
536 gid = os.getgid()
537 try:
538 return getgrgid(gid)[0]
539 except KeyError:
540 return str(gid)
541 except ImportError:
542 return None
543
512 # Platform specific variants
544 # Platform specific variants
513 if os.name == 'nt':
545 if os.name == 'nt':
514 demandload(globals(), "msvcrt")
546 demandload(globals(), "msvcrt")
General Comments 0
You need to be logged in to leave comments. Login now