1 # data.py -- Calculated/Discovered Data Values
2 # Copyright 1998-2013 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
5 import os, pwd, grp, platform, sys
8 portage.proxy.lazyimport.lazyimport(globals(),
9 'portage.output:colorize',
10 'portage.util:writemsg',
13 from portage.localization import _
15 ostype=platform.system()
17 if ostype == "DragonFly" or ostype.endswith("BSD"):
22 lchown = getattr(os, "lchown", None)
25 if ostype == "Darwin":
26 def lchown(*pos_args, **key_args):
29 def lchown(*pargs, **kwargs):
30 writemsg(colorize("BAD", "!!!") + _(
31 " It seems that os.lchown does not"
32 " exist. Please rebuild python.\n"), noiselevel=-1)
35 lchown = portage._chown_func_wrapper(lchown)
37 def portage_group_warning():
38 warn_prefix = colorize("BAD", "*** WARNING *** ")
40 "For security reasons, only system administrators should be",
41 "allowed in the portage group. Untrusted users or processes",
42 "can potentially exploit the portage group for attacks such as",
43 "local privilege escalation."
46 writemsg(warn_prefix, noiselevel=-1)
47 writemsg(x, noiselevel=-1)
48 writemsg("\n", noiselevel=-1)
49 writemsg("\n", noiselevel=-1)
51 # Portage has 3 security levels that depend on the uid and gid of the main
52 # process and are assigned according to the following table:
54 # Privileges secpass uid gid
56 # group 1 any portage_gid
59 # If the "wheel" group does not exist then wheelgid falls back to 0.
60 # If the "portage" group does not exist then portage_uid falls back to wheelgid.
66 wheelgid=grp.getgrnam("wheel")[2]
70 # The portage_uid and portage_gid global constants, and others that
71 # depend on them are initialized lazily, in order to allow configuration
72 # via make.conf. Eventually, these constants may be deprecated in favor
73 # of config attributes, since it's conceivable that multiple
74 # configurations with different constants could be used simultaneously.
75 _initialized_globals = set()
78 if k in _initialized_globals:
81 if k in ('portage_gid', 'portage_uid', 'secpass'):
82 global portage_gid, portage_uid, secpass
86 elif portage.const.EPREFIX:
88 #Discover the uid and gid of the portage user/group
91 portage_uid = pwd.getpwnam(_get_global('_portage_username')).pw_uid
97 portage_gid = grp.getgrnam(_get_global('_portage_grpname')).gr_gid
102 if secpass < 1 and portage_gid in os.getgroups():
105 # Suppress this error message if both PORTAGE_GRPNAME and
106 # PORTAGE_USERNAME are set to "root", for things like
107 # Android (see bug #454060).
108 if keyerror and not (_get_global('_portage_username') == "root" and
109 _get_global('_portage_grpname') == "root"):
110 writemsg(colorize("BAD",
111 _("portage: 'portage' user or group missing.")) + "\n", noiselevel=-1)
113 " For the defaults, line 1 goes into passwd, "
114 "and 2 into group.\n"), noiselevel=-1)
115 writemsg(colorize("GOOD",
116 " portage:x:250:250:portage:/var/tmp/portage:/bin/false") \
117 + "\n", noiselevel=-1)
118 writemsg(colorize("GOOD", " portage::250:portage") + "\n",
120 portage_group_warning()
122 _initialized_globals.add('portage_gid')
123 _initialized_globals.add('portage_uid')
124 _initialized_globals.add('secpass')
126 if k == 'portage_gid':
128 elif k == 'portage_uid':
133 raise AssertionError('unknown name: %s' % k)
135 elif k == 'userpriv_groups':
138 # Get a list of group IDs for the portage user. Do not use
139 # grp.getgrall() since it is known to trigger spurious
140 # SIGPIPE problems with nss_ldap.
141 cmd = ["id", "-G", _portage_username]
143 if sys.hexversion < 0x3020000 and sys.hexversion >= 0x3000000:
144 # Python 3.1 _execvp throws TypeError for non-absolute executable
145 # path passed as bytes (see http://bugs.python.org/issue8513).
146 fullname = portage.process.find_binary(cmd[0])
149 _initialized_globals.add(k)
153 encoding = portage._encodings['content']
154 cmd = [portage._unicode_encode(x,
155 encoding=encoding, errors='strict') for x in cmd]
156 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
157 stderr=subprocess.STDOUT)
158 myoutput = proc.communicate()[0]
160 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
161 for x in portage._unicode_decode(myoutput,
162 encoding=encoding, errors='strict').split():
169 # Avoid instantiating portage.settings when the desired
170 # variable is set in os.environ.
171 elif k in ('_portage_grpname', '_portage_username'):
173 if k == '_portage_grpname':
174 env_key = 'PORTAGE_GRPNAME'
176 env_key = 'PORTAGE_USERNAME'
178 if env_key in os.environ:
179 v = os.environ[env_key]
180 elif hasattr(portage, 'settings'):
181 v = portage.settings.get(env_key)
182 elif portage.const.EPREFIX:
183 # For prefix environments, default to the UID and GID of
184 # the top-level EROOT directory. The config class has
185 # equivalent code, but we also need to do it here if
186 # _disable_legacy_globals() has been called.
187 eroot = os.path.join(os.environ.get('ROOT', os.sep),
188 portage.const.EPREFIX.lstrip(os.sep))
190 eroot_st = os.stat(eroot)
194 if k == '_portage_grpname':
196 grp_struct = grp.getgrgid(eroot_st.st_gid)
200 v = grp_struct.gr_name
203 pwd_struct = pwd.getpwuid(eroot_st.st_uid)
207 v = pwd_struct.pw_name
212 raise AssertionError('unknown name: %s' % k)
215 _initialized_globals.add(k)
218 class _GlobalProxy(portage.proxy.objectproxy.ObjectProxy):
220 __slots__ = ('_name',)
222 def __init__(self, name):
223 portage.proxy.objectproxy.ObjectProxy.__init__(self)
224 object.__setattr__(self, '_name', name)
226 def _get_target(self):
227 return _get_global(object.__getattribute__(self, '_name'))
229 for k in ('portage_gid', 'portage_uid', 'secpass', 'userpriv_groups',
230 '_portage_grpname', '_portage_username'):
231 globals()[k] = _GlobalProxy(k)
236 Use config variables like PORTAGE_GRPNAME and PORTAGE_USERNAME to
237 initialize global variables. This allows settings to come from make.conf
238 instead of requiring them to be set in the calling environment.
240 if '_portage_grpname' not in _initialized_globals and \
241 '_portage_username' not in _initialized_globals:
243 # Prevents "TypeError: expected string" errors
244 # from grp.getgrnam() with PyPy
245 native_string = platform.python_implementation() == 'PyPy'
247 v = settings.get('PORTAGE_GRPNAME', 'portage')
249 v = portage._native_string(v)
250 globals()['_portage_grpname'] = v
251 _initialized_globals.add('_portage_grpname')
253 v = settings.get('PORTAGE_USERNAME', 'portage')
255 v = portage._native_string(v)
256 globals()['_portage_username'] = v
257 _initialized_globals.add('_portage_username')