##// END OF EJS Templates
Only read .hg/hgrc files from trusted users/groups...
Alexis S. L. Carvalho -
r3551:3b07e223 default
parent child Browse files
Show More
@@ -0,0 +1,113 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', debug=False):
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 parentui.updateopts(debug=debug)
57 u = ui.ui(parentui=parentui)
58 u.readconfig('.hg/hgrc')
59 for name, path in u.configitems('paths'):
60 print ' ', name, '=', path
61 print
62
63 return u
64
65 os.mkdir('repo')
66 os.chdir('repo')
67 os.mkdir('.hg')
68 f = open('.hg/hgrc', 'w')
69 f.write('[paths]\n')
70 f.write('local = /another/path\n\n')
71 f.close()
72
73 #print '# Everything is run by user foo, group bar\n'
74
75 # same user, same group
76 testui()
77 # same user, different group
78 testui(group='def')
79 # different user, same group
80 testui(user='abc')
81 # ... but we trust the group
82 testui(user='abc', tgroups=['bar'])
83 # different user, different group
84 testui(user='abc', group='def')
85 # ... but we trust the user
86 testui(user='abc', group='def', tusers=['abc'])
87 # ... but we trust the group
88 testui(user='abc', group='def', tgroups=['def'])
89 # ... but we trust the user and the group
90 testui(user='abc', group='def', tusers=['abc'], tgroups=['def'])
91 # ... but we trust all users
92 print '# we trust all users'
93 testui(user='abc', group='def', tusers=['*'])
94 # ... but we trust all groups
95 print '# we trust all groups'
96 testui(user='abc', group='def', tgroups=['*'])
97 # ... but we trust the whole universe
98 print '# we trust all users and groups'
99 testui(user='abc', group='def', tusers=['*'], tgroups=['*'])
100 # ... check that users and groups are in different namespaces
101 print "# we don't get confused by users and groups with the same name"
102 testui(user='abc', group='def', tusers=['def'], tgroups=['abc'])
103 # ... lists of user names work
104 print "# list of user names"
105 testui(user='abc', group='def', tusers=['foo', 'xyz', 'abc', 'bleh'],
106 tgroups=['bar', 'baz', 'qux'])
107 # ... lists of group names work
108 print "# list of group names"
109 testui(user='abc', group='def', tusers=['foo', 'xyz', 'bleh'],
110 tgroups=['bar', 'def', 'baz', 'qux'])
111
112 print "# Can't figure out the name of the user running this process"
113 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 ------
@@ -364,6 +366,17 b' server::'
364 6Mbps), uncompressed streaming is slower, because of the extra
366 6Mbps), uncompressed streaming is slower, because of the extra
365 data transfer overhead. Default is False.
367 data transfer overhead. Default is False.
366
368
369 trusted::
370 Mercurial will only read the .hg/hgrc file from a repository if
371 it belongs to a trusted user or to a trusted group. This section
372 specifies what users and groups are trusted. The current user is
373 always trusted. To trust everybody, list a user or a group with
374 name "*".
375 users;;
376 Comma-separated list of trusted users.
377 groups;;
378 Comma-separated list of trusted groups.
379
367 ui::
380 ui::
368 User interface controls.
381 User interface controls.
369 debug;;
382 debug;;
@@ -39,6 +39,8 b' class ui(object):'
39 self.debugflag = debug
39 self.debugflag = debug
40 self.interactive = interactive
40 self.interactive = interactive
41 self.traceback = traceback
41 self.traceback = traceback
42 self.trusted_users = {}
43 self.trusted_groups = {}
42 self.cdata = util.configparser()
44 self.cdata = util.configparser()
43 self.readconfig(util.rcpath())
45 self.readconfig(util.rcpath())
44 self.updateopts(verbose, debug, quiet, interactive)
46 self.updateopts(verbose, debug, quiet, interactive)
@@ -46,6 +48,8 b' class ui(object):'
46 # parentui may point to an ui object which is already a child
48 # parentui may point to an ui object which is already a child
47 self.parentui = parentui.parentui or parentui
49 self.parentui = parentui.parentui or parentui
48 self.readhooks = self.parentui.readhooks[:]
50 self.readhooks = self.parentui.readhooks[:]
51 self.trusted_users = parentui.trusted_users.copy()
52 self.trusted_groups = parentui.trusted_groups.copy()
49 self.cdata = dupconfig(self.parentui.cdata)
53 self.cdata = dupconfig(self.parentui.cdata)
50 if self.parentui.overlay:
54 if self.parentui.overlay:
51 self.overlay = dupconfig(self.parentui.overlay)
55 self.overlay = dupconfig(self.parentui.overlay)
@@ -82,12 +86,32 b' class ui(object):'
82 elif self.verbose and self.quiet:
86 elif self.verbose and self.quiet:
83 self.quiet = self.verbose = False
87 self.quiet = self.verbose = False
84
88
89 def _is_trusted(self, fp, f, warn=True):
90 tusers = self.trusted_users
91 tgroups = self.trusted_groups
92 if (tusers or tgroups) and '*' not in tusers and '*' not in tgroups:
93 st = util.fstat(fp)
94 user = util.username(st.st_uid)
95 group = util.groupname(st.st_gid)
96 if user not in tusers and group not in tgroups:
97 if warn:
98 self.warn(_('Not reading file %s from untrusted '
99 'user %s, group %s\n') % (f, user, group))
100 return False
101 return True
102
85 def readconfig(self, fn, root=None):
103 def readconfig(self, fn, root=None):
86 if isinstance(fn, basestring):
104 if isinstance(fn, basestring):
87 fn = [fn]
105 fn = [fn]
88 for f in fn:
106 for f in fn:
89 try:
107 try:
90 self.cdata.read(f)
108 fp = open(f)
109 except IOError:
110 continue
111 if not self._is_trusted(fp, f):
112 continue
113 try:
114 self.cdata.readfp(fp, f)
91 except ConfigParser.ParsingError, inst:
115 except ConfigParser.ParsingError, inst:
92 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
116 raise util.Abort(_("Failed to parse %s\n%s") % (f, inst))
93 # override data from config files with data set with ui.setconfig
117 # override data from config files with data set with ui.setconfig
@@ -144,6 +168,16 b' class ui(object):'
144 if name is None or name == 'interactive':
168 if name is None or name == 'interactive':
145 self.interactive = self.configbool("ui", "interactive", True)
169 self.interactive = self.configbool("ui", "interactive", True)
146
170
171 # update trust information
172 if section is None or section == 'trusted':
173 user = util.username()
174 if user is not None:
175 self.trusted_users[user] = 1
176 for user in self.configlist('trusted', 'users'):
177 self.trusted_users[user] = 1
178 for group in self.configlist('trusted', 'groups'):
179 self.trusted_groups[group] = 1
180
147 def setconfig(self, section, name, value):
181 def setconfig(self, section, name, value):
148 if not self.overlay:
182 if not self.overlay:
149 self.overlay = util.configparser()
183 self.overlay = util.configparser()
@@ -519,6 +519,36 b' def is_win_9x():'
519 except AttributeError:
519 except AttributeError:
520 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
520 return os.name == 'nt' and 'command' in os.environ.get('comspec', '')
521
521
522 def username(uid=None):
523 """Return the name of the user with the given uid.
524
525 If uid is None, return the name of the current user."""
526 try:
527 import pwd
528 if uid is None:
529 uid = os.getuid()
530 try:
531 return pwd.getpwuid(uid)[0]
532 except KeyError:
533 return str(uid)
534 except ImportError:
535 return None
536
537 def groupname(gid=None):
538 """Return the name of the group with the given gid.
539
540 If gid is None, return the name of the current group."""
541 try:
542 import grp
543 if gid is None:
544 gid = os.getgid()
545 try:
546 return grp.getgrgid(gid)[0]
547 except KeyError:
548 return str(gid)
549 except ImportError:
550 return None
551
522 # Platform specific variants
552 # Platform specific variants
523 if os.name == 'nt':
553 if os.name == 'nt':
524 demandload(globals(), "msvcrt")
554 demandload(globals(), "msvcrt")
General Comments 0
You need to be logged in to leave comments. Login now