1 # data.py -- Calculated/Discovered Data Values
2 # Copyright 1998-2011 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
5 import os, pwd, grp, platform
8 portage.proxy.lazyimport.lazyimport(globals(),
9 'portage.output:colorize',
10 'portage.util:writemsg',
12 from portage.localization import _
14 ostype=platform.system()
16 if ostype == "DragonFly" or ostype.endswith("BSD"):
21 lchown = getattr(os, "lchown", None)
24 if ostype == "Darwin":
25 def lchown(*pos_args, **key_args):
28 def lchown(*pargs, **kwargs):
29 writemsg(colorize("BAD", "!!!") + _(
30 " It seems that os.lchown does not"
31 " exist. Please rebuild python.\n"), noiselevel=-1)
34 lchown = portage._unicode_func_wrapper(lchown)
36 def portage_group_warning():
37 warn_prefix = colorize("BAD", "*** WARNING *** ")
39 "For security reasons, only system administrators should be",
40 "allowed in the portage group. Untrusted users or processes",
41 "can potentially exploit the portage group for attacks such as",
42 "local privilege escalation."
45 writemsg(warn_prefix, noiselevel=-1)
46 writemsg(x, noiselevel=-1)
47 writemsg("\n", noiselevel=-1)
48 writemsg("\n", noiselevel=-1)
50 # Portage has 3 security levels that depend on the uid and gid of the main
51 # process and are assigned according to the following table:
53 # Privileges secpass uid gid
55 # group 1 any portage_gid
58 # If the "wheel" group does not exist then wheelgid falls back to 0.
59 # If the "portage" group does not exist then portage_uid falls back to wheelgid.
65 wheelgid=grp.getgrnam("wheel")[2]
69 # The portage_uid and portage_gid global constants, and others that
70 # depend on them are initialized lazily, in order to allow configuration
71 # via make.conf. Eventually, these constants may be deprecated in favor
72 # of config attributes, since it's conceivable that multiple
73 # configurations with different constants could be used simultaneously.
74 _initialized_globals = set()
77 if k in _initialized_globals:
80 if k in ('portage_gid', 'portage_uid', 'secpass'):
81 global portage_gid, portage_uid, secpass
85 elif portage.const.EPREFIX:
87 #Discover the uid and gid of the portage user/group
89 portage_uid = pwd.getpwnam(_get_global('_portage_username')).pw_uid
90 _portage_grpname = _get_global('_portage_grpname')
91 if platform.python_implementation() == 'PyPy':
92 # Somehow this prevents "TypeError: expected string" errors
93 # from grp.getgrnam() with PyPy 1.7
94 _portage_grpname = str(_portage_grpname)
95 portage_gid = grp.getgrnam(_portage_grpname).gr_gid
96 if secpass < 1 and portage_gid in os.getgroups():
101 writemsg(colorize("BAD",
102 _("portage: 'portage' user or group missing.")) + "\n", noiselevel=-1)
104 " For the defaults, line 1 goes into passwd, "
105 "and 2 into group.\n"), noiselevel=-1)
106 writemsg(colorize("GOOD",
107 " portage:x:250:250:portage:/var/tmp/portage:/bin/false") \
108 + "\n", noiselevel=-1)
109 writemsg(colorize("GOOD", " portage::250:portage") + "\n",
111 portage_group_warning()
113 _initialized_globals.add('portage_gid')
114 _initialized_globals.add('portage_uid')
115 _initialized_globals.add('secpass')
117 if k == 'portage_gid':
119 elif k == 'portage_uid':
124 raise AssertionError('unknown name: %s' % k)
126 elif k == 'userpriv_groups':
129 # Get a list of group IDs for the portage user. Do not use
130 # grp.getgrall() since it is known to trigger spurious
131 # SIGPIPE problems with nss_ldap.
132 mystatus, myoutput = \
133 portage.subprocess_getstatusoutput("id -G %s" % _portage_username)
134 if mystatus == os.EX_OK:
135 for x in myoutput.split():
142 # Avoid instantiating portage.settings when the desired
143 # variable is set in os.environ.
144 elif k in ('_portage_grpname', '_portage_username'):
146 if k == '_portage_grpname':
147 env_key = 'PORTAGE_GRPNAME'
149 env_key = 'PORTAGE_USERNAME'
151 if env_key in os.environ:
152 v = os.environ[env_key]
153 elif hasattr(portage, 'settings'):
154 v = portage.settings.get(env_key)
155 elif portage.const.EPREFIX:
156 # For prefix environments, default to the UID and GID of
157 # the top-level EROOT directory. The config class has
158 # equivalent code, but we also need to do it here if
159 # _disable_legacy_globals() has been called.
160 eroot = os.path.join(os.environ.get('ROOT', os.sep),
161 portage.const.EPREFIX.lstrip(os.sep))
163 eroot_st = os.stat(eroot)
167 if k == '_portage_grpname':
169 grp_struct = grp.getgrgid(eroot_st.st_gid)
173 v = grp_struct.gr_name
176 pwd_struct = pwd.getpwuid(eroot_st.st_uid)
180 v = pwd_struct.pw_name
185 raise AssertionError('unknown name: %s' % k)
188 _initialized_globals.add(k)
191 class _GlobalProxy(portage.proxy.objectproxy.ObjectProxy):
193 __slots__ = ('_name',)
195 def __init__(self, name):
196 portage.proxy.objectproxy.ObjectProxy.__init__(self)
197 object.__setattr__(self, '_name', name)
199 def _get_target(self):
200 return _get_global(object.__getattribute__(self, '_name'))
202 for k in ('portage_gid', 'portage_uid', 'secpass', 'userpriv_groups',
203 '_portage_grpname', '_portage_username'):
204 globals()[k] = _GlobalProxy(k)
209 Use config variables like PORTAGE_GRPNAME and PORTAGE_USERNAME to
210 initialize global variables. This allows settings to come from make.conf
211 instead of requiring them to be set in the calling environment.
213 if '_portage_grpname' not in _initialized_globals and \
214 '_portage_username' not in _initialized_globals:
216 v = settings.get('PORTAGE_GRPNAME', 'portage')
217 globals()['_portage_grpname'] = v
218 _initialized_globals.add('_portage_grpname')
220 v = settings.get('PORTAGE_USERNAME', 'portage')
221 globals()['_portage_username'] = v
222 _initialized_globals.add('_portage_username')