config: init data/output modules in constructor
[portage.git] / pym / portage / data.py
1 # data.py -- Calculated/Discovered Data Values
2 # Copyright 1998-2010 Gentoo Foundation
3 # Distributed under the terms of the GNU General Public License v2
4
5 import os, pwd, grp, platform
6
7 import portage
8 portage.proxy.lazyimport.lazyimport(globals(),
9         'portage.output:colorize',
10         'portage.util:writemsg',
11 )
12 from portage.localization import _
13
14 ostype=platform.system()
15 userland = None
16 if ostype == "DragonFly" or ostype.endswith("BSD"):
17         userland = "BSD"
18 else:
19         userland = "GNU"
20
21 lchown = getattr(os, "lchown", None)
22
23 if not lchown:
24         if ostype == "Darwin":
25                 def lchown(*pos_args, **key_args):
26                         pass
27         else:
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)
32                 lchown()
33
34 lchown = portage._unicode_func_wrapper(lchown)
35
36 def portage_group_warning():
37         warn_prefix = colorize("BAD", "*** WARNING ***  ")
38         mylines = [
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."
43         ]
44         for x in mylines:
45                 writemsg(warn_prefix, noiselevel=-1)
46                 writemsg(x, noiselevel=-1)
47                 writemsg("\n", noiselevel=-1)
48         writemsg("\n", noiselevel=-1)
49
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:
52 #
53 # Privileges  secpass  uid    gid
54 # normal      0        any    any
55 # group       1        any    portage_gid
56 # super       2        0      any
57 #
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.
60
61 uid=os.getuid()
62 wheelgid=0
63
64 try:
65         wheelgid=grp.getgrnam("wheel")[2]
66 except KeyError:
67         pass
68
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()
75
76 def _get_global(k):
77         if k in _initialized_globals:
78                 return globals()[k]
79
80         if k in ('portage_gid', 'portage_uid', 'secpass'):
81                 global portage_gid, portage_uid, secpass
82                 secpass = 0
83                 if uid == 0:
84                         secpass = 2
85                 elif portage.const.EPREFIX:
86                         secpass = 2
87                 #Discover the uid and gid of the portage user/group
88                 try:
89                         portage_uid = pwd.getpwnam(_get_global('_portage_uname')).pw_uid
90                         portage_gid = grp.getgrnam(_get_global('_portage_grpname')).gr_gid
91                         if secpass < 1 and portage_gid in os.getgroups():
92                                 secpass = 1
93                 except KeyError:
94                         portage_uid = 0
95                         portage_gid = 0
96                         writemsg(colorize("BAD",
97                                 _("portage: 'portage' user or group missing.")) + "\n", noiselevel=-1)
98                         writemsg(_(
99                                 "         For the defaults, line 1 goes into passwd, "
100                                 "and 2 into group.\n"), noiselevel=-1)
101                         writemsg(colorize("GOOD",
102                                 "         portage:x:250:250:portage:/var/tmp/portage:/bin/false") \
103                                 + "\n", noiselevel=-1)
104                         writemsg(colorize("GOOD", "         portage::250:portage") + "\n",
105                                 noiselevel=-1)
106                         portage_group_warning()
107
108                 _initialized_globals.add('portage_gid')
109                 _initialized_globals.add('portage_uid')
110                 _initialized_globals.add('secpass')
111
112                 if k == 'portage_gid':
113                         return portage_gid
114                 elif k == 'portage_uid':
115                         return portage_uid
116                 elif k == 'secpass':
117                         return secpass
118                 else:
119                         raise AssertionError('unknown name: %s' % k)
120
121         elif k == 'userpriv_groups':
122                 v = [portage_gid]
123                 if secpass >= 2:
124                         # Get a list of group IDs for the portage user. Do not use
125                         # grp.getgrall() since it is known to trigger spurious
126                         # SIGPIPE problems with nss_ldap.
127                         mystatus, myoutput = \
128                                 portage.subprocess_getstatusoutput("id -G %s" % _portage_uname)
129                         if mystatus == os.EX_OK:
130                                 for x in myoutput.split():
131                                         try:
132                                                 v.append(int(x))
133                                         except ValueError:
134                                                 pass
135                                 v = sorted(set(v))
136
137         elif k == '_portage_grpname':
138                 env = getattr(portage, 'settings', os.environ)
139                 v = env.get('PORTAGE_GRPNAME', 'portage')
140         elif k == '_portage_uname':
141                 env = getattr(portage, 'settings', os.environ)
142                 v = env.get('PORTAGE_USERNAME', 'portage')
143         else:
144                 raise AssertionError('unknown name: %s' % k)
145
146         globals()[k] = v
147         _initialized_globals.add(k)
148         return v
149
150 class _GlobalProxy(portage.proxy.objectproxy.ObjectProxy):
151
152         __slots__ = ('_name',)
153
154         def __init__(self, name):
155                 portage.proxy.objectproxy.ObjectProxy.__init__(self)
156                 object.__setattr__(self, '_name', name)
157
158         def _get_target(self):
159                 return _get_global(object.__getattribute__(self, '_name'))
160
161 for k in ('portage_gid', 'portage_uid', 'secpass', 'userpriv_groups',
162         '_portage_grpname', '_portage_uname'):
163         globals()[k] = _GlobalProxy(k)
164 del k
165
166 def _init(settings):
167         """
168         Use config variables like PORTAGE_GRPNAME and PORTAGE_USERNAME to
169         initialize global variables. This allows settings to come from make.conf
170         instead of requiring them to be set in the calling environment.
171         """
172         if '_portage_grpname' not in _initialized_globals and \
173                 '_portage_uname' not in _initialized_globals:
174
175                 v = settings.get('PORTAGE_GRPNAME')
176                 if v is not None:
177                         globals()['_portage_grpname'] = v
178                         _initialized_globals.add('_portage_grpname')
179
180                 v = settings.get('PORTAGE_USERNAME')
181                 if v is not None:
182                         globals()['_portage_uname'] = v
183                         _initialized_globals.add('_portage_uname')