| Trees | Indices | Help |
|---|
|
|
1 # Spacewalk Proxy Server authentication manager.
2 #
3 # Copyright (c) 2008--2020 Red Hat, Inc.
4 #
5 # This software is licensed to you under the GNU General Public License,
6 # version 2 (GPLv2). There is NO WARRANTY for this software, express or
7 # implied, including the implied warranties of MERCHANTABILITY or FITNESS
8 # FOR A PARTICULAR PURPOSE. You should have received a copy of GPLv2
9 # along with this software; if not, see
10 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt.
11 #
12 # Red Hat trademarks are not licensed under GPLv2. No permission is
13 # granted to use or replicate Red Hat trademarks that are incorporated
14 # in this software or its documentation.
15 #
16 # -----------------------------------------------------------------------------
17
18 # system imports
19 import os
20 import time
21 import socket
22 import xmlrpclib
23 import sys
24 # pylint: disable=E0611
25 from hashlib import sha1
26
27 # common imports
28 from spacewalk.common.rhnLib import parseUrl
29 from spacewalk.common.rhnTB import Traceback
30 from spacewalk.common.rhnLog import log_debug, log_error
31 from spacewalk.common.rhnConfig import CFG
32 from spacewalk.common.rhnException import rhnFault
33 from spacewalk.common import rhnCache
34 from spacewalk.common.rhnTranslate import _
35
36 # local imports
37 from rhn import rpclib
38 from rhn import SSL
39 import rhnAuthCacheClient
40
41
42 sys.path.append('/usr/share/rhn')
43 from up2date_client import config # pylint: disable=E0012, C0413, wrong-import-order
44
45 # To avoid doing unnecessary work, keep ProxyAuth object global
46 __PROXY_AUTH = None
47 UP2DATE_CONFIG = config.Config('/etc/sysconfig/rhn/up2date')
51 global __PROXY_AUTH
52 if not __PROXY_AUTH:
53 __PROXY_AUTH = ProxyAuth(hostname)
54 if __PROXY_AUTH.hostname != hostname:
55 __PROXY_AUTH = ProxyAuth(hostname)
56 return __PROXY_AUTH
57
60
61 __serverid = None
62 __systemid = None
63 __systemid_mtime = None
64 __systemid_filename = UP2DATE_CONFIG['systemIdPath']
65
66 __nRetries = 3 # number of login retries
67
68 hostname = None
69
74
76 """ update the systemid/serverid but only if they stat differently.
77 returns 0=no updates made; or 1=updates were made
78 """
79 if not os.access(ProxyAuth.__systemid_filename, os.R_OK):
80 log_error("unable to access %s" % ProxyAuth.__systemid_filename)
81 raise rhnFault(1000,
82 _("Spacewalk Proxy error (Spacewalk Proxy systemid has wrong permissions?). "
83 "Please contact your system administrator."))
84
85 mtime = None
86 try:
87 mtime = os.stat(ProxyAuth.__systemid_filename)[-2]
88 except IOError, e:
89 log_error("unable to stat %s: %s" % (ProxyAuth.__systemid_filename, repr(e)))
90 raise rhnFault(1000,
91 _("Spacewalk Proxy error (Spacewalk Proxy systemid has wrong permissions?). "
92 "Please contact your system administrator.")), None, sys.exc_info()[2]
93
94 if not self.__systemid_mtime:
95 ProxyAuth.__systemid_mtime = mtime
96
97 if self.__systemid_mtime == mtime \
98 and self.__systemid and self.__serverid:
99 # nothing to do
100 return 0
101
102 # get systemid
103 try:
104 ProxyAuth.__systemid = open(ProxyAuth.__systemid_filename, 'r').read()
105 except IOError, e:
106 log_error("unable to read %s" % ProxyAuth.__systemid_filename)
107 raise rhnFault(1000,
108 _("Spacewalk Proxy error (Spacewalk Proxy systemid has wrong permissions?). "
109 "Please contact your system administrator.")), None, sys.exc_info()[2]
110
111 # get serverid
112 sysid, _cruft = xmlrpclib.loads(ProxyAuth.__systemid)
113 ProxyAuth.__serverid = sysid[0]['system_id'][3:]
114
115 log_debug(7, 'SystemId: "%s[...snip snip...]%s"'
116 % (ProxyAuth.__systemid[:20], ProxyAuth.__systemid[-20:]))
117 log_debug(7, 'ServerId: %s' % ProxyAuth.__serverid)
118
119 # ids were updated
120 return 1
121
126
128 """ check cache, login if need be, and cache.
129 """
130 log_debug(3)
131 oldToken = self.get_cached_token()
132 token = oldToken
133 if not token or forceRefresh or self.__processSystemid():
134 token = self.login()
135 if token and token != oldToken:
136 self.set_cached_token(token)
137 return token
138
140 """ Fetches this proxy's token (or None) from the cache
141 """
142 log_debug(3)
143 # Try to connect to the token-cache.
144 shelf = get_auth_shelf()
145 # Fetch the token
146 key = self.__cache_proxy_key()
147 if shelf.has_key(key):
148 return shelf[key]
149 return None
150
152 """ Caches current token in the auth cache.
153 """
154 log_debug(3)
155 # Try to connect to the token-cache.
156 shelf = get_auth_shelf()
157 # Cache the token.
158 try:
159 shelf[self.__cache_proxy_key()] = token
160 except:
161 text = _("""\
162 Caching of authentication token for proxy id %s failed!
163 Either the authentication caching daemon is experiencing
164 problems, isn't running, or the token is somehow corrupt.
165 """) % self.__serverid
166 Traceback("ProxyAuth.set_cached_token", extra=text)
167 raise rhnFault(1000,
168 _("Spacewalk Proxy error (auth caching issue). "
169 "Please contact your system administrator.")), None, sys.exc_info()[2]
170 log_debug(4, "successfully returning")
171 return token
172
174 """Removes the token from the cache
175 """
176 log_debug(3)
177 # Connect to the token cache
178 shelf = get_auth_shelf()
179 key = self.__cache_proxy_key()
180 try:
181 del shelf[key]
182 except KeyError:
183 # no problem
184 pass
185
187 """ Login and fetch new token (proxy token).
188
189 How it works in a nutshell.
190 Only the broker component uses this. We perform a xmlrpc request
191 to rhn_parent. This occurs outside of the http process we are
192 currently working on. So, we do this all on our own; do all of
193 our own SSL decisionmaking etc. We use CFG.RHN_PARENT as we always
194 bypass the SSL redirect.
195
196 DESIGN NOTES: what is the proxy auth token?
197 -------------------------------------------
198 An Spacewalk Proxy auth token is a token fetched upon login from
199 Red Hat Satellite or hosted.
200
201 It has this format:
202 'S:U:ST:EO:SIG'
203 Where:
204 S = server ID
205 U = username
206 ST = server time
207 EO = expiration offset
208 SIG = signature
209 H = hostname (important later)
210
211 Within this function within the Spacewalk Proxy Broker we also tag on
212 the hostname to the end of the token. The token as described above
213 is enough for authentication purposes, but we need a to identify
214 the exact hostname (as the Spacewalk Proxy sees it). So now the token
215 becomes (token:hostname):
216 'S:U:ST:EO:SIG:H'
217
218 DESIGN NOTES: what is X-RHN-Proxy-Auth?
219 -------------------------------------------
220 This is where we use the auth token beyond Spacewalk Proxy login
221 purposes. This a header used to track request routes through
222 a hierarchy of RHN Proxies.
223
224 X-RHN-Proxy-Auth is a header that passes proxy authentication
225 information around in the form of an ordered list of tokens. This
226 list is used to gain information as to how a client request is
227 routed throughout an RHN topology.
228
229 Format: 'S1:U1:ST1:EO1:SIG1:H1,S2:U2:ST2:EO2:SIG2:H2,...'
230 |_________1_________| |_________2_________| |__...
231 token token
232 where token is really: token:hostname
233
234 leftmost token was the first token hit by a client request.
235 rightmost token was the last token hit by a client request.
236
237 """
238 # pylint: disable=R0915
239
240 log_debug(3)
241 server = self.__getXmlrpcServer()
242 error = None
243 token = None
244 # update the systemid/serverid if need be.
245 self.__processSystemid()
246 # Makes three attempts to login
247 for _i in range(self.__nRetries):
248 try:
249 token = server.proxy.login(self.__systemid)
250 except (socket.error, socket.sslerror), e:
251 if CFG.HTTP_PROXY:
252 # socket error, check to see if your HTTP proxy is running...
253 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
254 httpProxy, httpProxyPort = CFG.HTTP_PROXY.split(':')
255 try:
256 s.connect((httpProxy, int(httpProxyPort)))
257 except socket.error, e:
258 error = ['socket.error', 'HTTP Proxy not running? '
259 '(%s) %s' % (CFG.HTTP_PROXY, e)]
260 # rather big problem: http proxy not running.
261 log_error("*** ERROR ***: %s" % error[1])
262 Traceback(mail=0)
263 except socket.sslerror, e:
264 error = ['socket.sslerror',
265 '(%s) %s' % (CFG.HTTP_PROXY, e)]
266 # rather big problem: http proxy not running.
267 log_error("*** ERROR ***: %s" % error[1])
268 Traceback(mail=0)
269 else:
270 error = ['socket', str(e)]
271 log_error(error)
272 Traceback(mail=0)
273 else:
274 log_error("Socket error", e)
275 Traceback(mail=0)
276 Traceback(mail=1)
277 token = None
278 time.sleep(.25)
279 continue
280 except SSL.SSL.Error, e:
281 token = None
282 error = ['rhn.SSL.SSL.Error', repr(e), str(e)]
283 log_error(error)
284 Traceback(mail=0)
285 time.sleep(.25)
286 continue
287 except xmlrpclib.ProtocolError, e:
288 token = None
289 log_error('xmlrpclib.ProtocolError', e)
290 time.sleep(.25)
291 continue
292 except xmlrpclib.Fault, e:
293 # Report it through the mail
294 # Traceback will try to walk over all the values
295 # in each stack frame, and eventually will try to stringify
296 # the method object itself
297 # This should trick it, since the originator of the exception
298 # is this function, instead of a deep call into xmlrpclib
299 log_error("%s" % e)
300 if e.faultCode == 10000:
301 # reraise it for the users (outage or "important message"
302 # coming through")
303 raise rhnFault(e.faultCode, e.faultString), None, sys.exc_info()[2]
304 # ok... it's some other fault
305 Traceback("ProxyAuth.login (Fault) - Spacewalk Proxy not "
306 "able to log in.")
307 # And raise a Proxy Error - the server made its point loud and
308 # clear
309 raise rhnFault(1000,
310 _("Spacewalk Proxy error (during proxy login). "
311 "Please contact your system administrator.")), None, sys.exc_info()[2]
312 except Exception, e: # pylint: disable=E0012, W0703
313 token = None
314 log_error("Unhandled exception", e)
315 Traceback(mail=0)
316 time.sleep(.25)
317 continue
318 else:
319 break
320
321 if not token:
322 if error:
323 if error[0] in ('xmlrpclib.ProtocolError', 'socket.error', 'socket'):
324 raise rhnFault(1000,
325 _("Spacewalk Proxy error (error: %s). "
326 "Please contact your system administrator.") % error[0])
327 if error[0] in ('rhn.SSL.SSL.Error', 'socket.sslerror'):
328 raise rhnFault(1000,
329 _("Spacewalk Proxy error (SSL issues? Error: %s). "
330 "Please contact your system administrator.") % error[0])
331 else:
332 raise rhnFault(1002, err_text='%s' % e)
333 else:
334 raise rhnFault(1001)
335 if self.hostname:
336 token = token + ':' + self.hostname
337 log_debug(6, "New proxy token: %s" % token)
338 return token
339
340 @staticmethod
342 shelf = get_auth_shelf()
343 if shelf.has_key(clientid):
344 return shelf[clientid]
345 return None
346
347 @staticmethod
351
353 # Maybe a load-balanced proxie and client logged in through a
354 # different one? Ask upstream if token is valid. If it is,
355 # upate cache.
356 # copy to simple dict for transmission. :-/
357 dumbToken = {}
358 satInfo = None
359 for key in ('X-RHN-Server-Id', 'X-RHN-Auth-User-Id', 'X-RHN-Auth',
360 'X-RHN-Auth-Server-Time', 'X-RHN-Auth-Expire-Offset'):
361 if token.has_key(key):
362 dumbToken[key] = token[key]
363 try:
364 s = self.__getXmlrpcServer()
365 satInfo = s.proxy.checkTokenValidity(
366 dumbToken, self.get_system_id())
367 except Exception: # pylint: disable=E0012, W0703
368 pass # Satellite is not updated enough, keep old behavior
369
370 # False if not valid token, a dict of info we need otherwise
371 # We have to calculate the proxy-clock-skew between Sat and this
372 # Proxy, as well as store the subscribed channels for this client
373 # (which the client does not pass up in headers and which we
374 # wouldn't trust even if it did).
375 if satInfo:
376 clockSkew = time.time() - float(satInfo['X-RHN-Auth-Server-Time'])
377 dumbToken['X-RHN-Auth-Proxy-Clock-Skew'] = clockSkew
378 dumbToken['X-RHN-Auth-Channels'] = satInfo['X-RHN-Auth-Channels']
379 # update our cache so we don't have to ask next time
380 self.set_client_token(clientid, dumbToken)
381 return dumbToken
382 return None
383
384 # __private methods__
385
386 @staticmethod
388 """ get an xmlrpc server object
389
390 WARNING: if CFG.USE_SSL is off, we are sending info
391 in the clear.
392 """
393 log_debug(3)
394
395 # build the URL
396 url = CFG.RHN_PARENT or ''
397 url = parseUrl(url)[1].split(':')[0]
398 if CFG.USE_SSL:
399 url = 'https://' + url + '/XMLRPC'
400 else:
401 url = 'http://' + url + '/XMLRPC'
402 log_debug(3, 'server url: %s' % url)
403
404 if CFG.HTTP_PROXY:
405 serverObj = rpclib.Server(url,
406 proxy=CFG.HTTP_PROXY,
407 username=CFG.HTTP_PROXY_USERNAME,
408 password=CFG.HTTP_PROXY_PASSWORD)
409 else:
410 serverObj = rpclib.Server(url)
411 if CFG.USE_SSL and CFG.CA_CHAIN:
412 if not os.access(CFG.CA_CHAIN, os.R_OK):
413 log_error('ERROR: missing or cannot access (for ca_chain): %s' % CFG.CA_CHAIN)
414 raise rhnFault(1000,
415 _("Spacewalk Proxy error (file access issues). "
416 "Please contact your system administrator. "
417 "Please refer to Spacewalk Proxy logs."))
418 serverObj.add_trusted_cert(CFG.CA_CHAIN)
419 serverObj.add_header('X-RHN-Client-Version', 2)
420 return serverObj
421
424
426 return self.__serverid
427
430 if CFG.USE_LOCAL_AUTH:
431 return AuthLocalBackend()
432 server, port = CFG.AUTH_CACHE_SERVER.split(':')
433 port = int(port)
434 return rhnAuthCacheClient.Shelf((server, port))
435
438 _cache_prefix = "proxy-auth"
439
442
446
448 rkey = self._compute_key(key)
449 # We want a dictionary-like behaviour, so if the key is not present,
450 # raise an exception (that's what missing_is_null=0 does)
451 val = rhnCache.get(rkey, missing_is_null=0)
452 return val
453
457
461
464
467
468 # ==============================================================================
469
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Wed Mar 4 07:37:45 2020 | http://epydoc.sourceforge.net |