1 # data.py -- Calculated/Discovered Data Values
2 # Copyright 1998-2012 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._unicode_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
90 portage_uid = pwd.getpwnam(_get_global('_portage_username')).pw_uid
91 _portage_grpname = _get_global('_portage_grpname')
92 if platform.python_implementation() == 'PyPy':
93 # Somehow this prevents "TypeError: expected string" errors
94 # from grp.getgrnam() with PyPy 1.7
95 _portage_grpname = str(_portage_grpname)
96 portage_gid = grp.getgrnam(_portage_grpname).gr_gid
97 if secpass < 1 and portage_gid in os.getgroups():
102 writemsg(colorize("BAD",
103 _("portage: 'portage' user or group missing.")) + "\n", noiselevel=-1)
105 " For the defaults, line 1 goes into passwd, "
106 "and 2 into group.\n"), noiselevel=-1)
107 writemsg(colorize("GOOD",
108 " portage:x:250:250:portage:/var/tmp/portage:/bin/false") \
109 + "\n", noiselevel=-1)
110 writemsg(colorize("GOOD", " portage::250:portage") + "\n",
112 portage_group_warning()
114 _initialized_globals.add('portage_gid')
115 _initialized_globals.add('portage_uid')
116 _initialized_globals.add('secpass')
118 if k == 'portage_gid':
120 elif k == 'portage_uid':
125 raise AssertionError('unknown name: %s' % k)
127 elif k == 'userpriv_groups':
130 # Get a list of group IDs for the portage user. Do not use
131 # grp.getgrall() since it is known to trigger spurious
132 # SIGPIPE problems with nss_ldap.
133 cmd = ["id", "-G", _portage_username]
134 encoding = portage._encodings['content']
135 if sys.hexversion < 0x3000000 or sys.hexversion >= 0x3020000:
136 # Python 3.1 does not support bytes in Popen args.
137 cmd = [portage._unicode_encode(x,
138 encoding=encoding, errors='strict')
140 proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
141 stderr=subprocess.STDOUT)
142 myoutput = proc.communicate()[0]
144 if os.WIFEXITED(status) and os.WEXITSTATUS(status) == os.EX_OK:
145 for x in portage._unicode_decode(myoutput,
146 encoding=encoding, errors='strict').split():
153 # Avoid instantiating portage.settings when the desired
154 # variable is set in os.environ.
155 elif k in ('_portage_grpname', '_portage_username'):
157 if k == '_portage_grpname':
158 env_key = 'PORTAGE_GRPNAME'
160 env_key = 'PORTAGE_USERNAME'
162 if env_key in os.environ:
163 v = os.environ[env_key]
164 elif hasattr(portage, 'settings'):
165 v = portage.settings.get(env_key)
166 elif portage.const.EPREFIX:
167 # For prefix environments, default to the UID and GID of
168 # the top-level EROOT directory. The config class has
169 # equivalent code, but we also need to do it here if
170 # _disable_legacy_globals() has been called.
171 eroot = os.path.join(os.environ.get('ROOT', os.sep),
172 portage.const.EPREFIX.lstrip(os.sep))
174 eroot_st = os.stat(eroot)
178 if k == '_portage_grpname':
180 grp_struct = grp.getgrgid(eroot_st.st_gid)
184 v = grp_struct.gr_name
187 pwd_struct = pwd.getpwuid(eroot_st.st_uid)
191 v = pwd_struct.pw_name
196 raise AssertionError('unknown name: %s' % k)
199 _initialized_globals.add(k)
202 class _GlobalProxy(portage.proxy.objectproxy.ObjectProxy):
204 __slots__ = ('_name',)
206 def __init__(self, name):
207 portage.proxy.objectproxy.ObjectProxy.__init__(self)
208 object.__setattr__(self, '_name', name)
210 def _get_target(self):
211 return _get_global(object.__getattribute__(self, '_name'))
213 for k in ('portage_gid', 'portage_uid', 'secpass', 'userpriv_groups',
214 '_portage_grpname', '_portage_username'):
215 globals()[k] = _GlobalProxy(k)
220 Use config variables like PORTAGE_GRPNAME and PORTAGE_USERNAME to
221 initialize global variables. This allows settings to come from make.conf
222 instead of requiring them to be set in the calling environment.
224 if '_portage_grpname' not in _initialized_globals and \
225 '_portage_username' not in _initialized_globals:
227 v = settings.get('PORTAGE_GRPNAME', 'portage')
228 globals()['_portage_grpname'] = v
229 _initialized_globals.add('_portage_grpname')
231 v = settings.get('PORTAGE_USERNAME', 'portage')
232 globals()['_portage_username'] = v
233 _initialized_globals.add('_portage_username')