def validate_new_password(password): """ Perform all the checks required for new passwords """ log_debug(3, "Entered validate_new_password") # # We're copying the code because we don't want to # invalidate any of the existing passwords. # # Validate password based on configurable length # regular expression if not password: raise rhnFault(12) if len(password) < CFG.MIN_PASSWD_LEN: raise rhnFault(14, _("password must be at least %d characters") % CFG.MIN_PASSWD_LEN) if len(password) > CFG.MAX_PASSWD_LEN: raise rhnFault(701, _("Password must be shorter than %d characters") % CFG.MAX_PASSWD_LEN) password = password[:CFG.MAX_PASSWD_LEN] invalid_re = re.compile( r"[^ A-Za-z0-9`!@#$%^&*()-_=+[{\]}\\|;:'\",<.>/?~]") asterisks_re = re.compile(r"^\**$") # make sure the password isn't all *'s tmp = asterisks_re.match(password) if tmp is not None: raise rhnFault(15, "password cannot be all asterisks '*'") # make sure we have only printable characters tmp = invalid_re.search(password) if tmp is not None: pos = tmp.regs[0] raise rhnFault(15, _("password contains character `%s'") % password[pos[1] - 1])
def getxml(self): # see if there were any template strings loaded from the db, # {label:value} templateOverrides = rhnFlags.get('templateOverrides') # update the templateValues in the module if templateOverrides: for label in templateOverrides.keys(): # only care about values we've defined defaults for... if label in templateValues: templateValues[label] = templateOverrides[label] s = StringIO() s.write("\n") if self.text: s.write(_("Error Message:\n %s\n") % self.text.strip()) if self.code: s.write(_("Error Class Code: %s\n") % self.code) if self.arrayText: cinfo = self.arrayText % templateValues s.write(_("Error Class Info: %s\n") % cinfo.rstrip()) if self.explain: s.write(_("Explanation: %s") % Explain) if not self.code: return xmlrpclib.Fault(1, s.getvalue()) return xmlrpclib.Fault(-self.code, s.getvalue())
def get_package_path(server_id, pkg_spec, channel): log_debug(3, server_id, pkg_spec, channel) if isinstance(pkg_spec, ListType): pkg = pkg_spec[:4] #Insert EPOCH pkg.insert(1, None) else: pkg = parseRPMFilename(pkg_spec) if pkg is None: log_debug(4, "Error", "Requested weird package", pkg_spec) raise rhnFault(17, _("Invalid RPM package %s requested") % pkg_spec) statement = """ select p.id, p.path path, pe.epoch epoch from rhnPackageArch pa, rhnChannelPackage cp, rhnPackage p, rhnPackageEVR pe, rhnServerChannel sc, rhnPackageName pn, rhnChannel c where 1=1 and c.label = :channel and pn.name = :name and sc.server_id = :server_id and pe.version = :ver and pe.release = :rel and c.id = sc.channel_id and c.id = cp.channel_id and pa.label = :arch and pn.id = p.name_id and p.id = cp.package_id and p.evr_id = pe.id and sc.channel_id = cp.channel_id and p.package_arch_id = pa.id """ h = rhnSQL.prepare(statement) pkg = map(str, pkg) h.execute(name = pkg[0], ver = pkg[2], rel = pkg[3], arch = pkg[4], channel = channel, server_id = server_id) rs = h.fetchall_dict() if not rs: log_debug(4, "Error", "Non-existant package requested", server_id, pkg_spec, channel) raise rhnFault(17, _("Invalid RPM package %s requested") % pkg_spec) # It is unlikely for this query to return more than one row, # but it is possible # (having two packages with the same n, v, r, a and different epoch in # the same channel is prohibited by the RPM naming scheme; but extra # care won't hurt) max_row = rs[0] for each in rs[1:]: # Compare the epoch as string if _none2emptyString(each['epoch']) > _none2emptyString(max_row['epoch']): max_row = each # Set the flag for the proxy download accelerator rhnFlags.set("Download-Accelerator-Path", max_row['path']) return check_package_file(max_row['path'], max_row['id'], pkg_spec), max_row['id']
def _verifyProxyAuthToken(auth_token): """ verifies the validity of a proxy auth token NOTE: X-RHN-Proxy-Auth described in proxy/broker/rhnProxyAuth.py """ log_debug(4, auth_token) token, hostname = splitProxyAuthToken(auth_token) hostname = hostname.strip() ipv4_regex = '^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$' # This ipv6 regex was develeoped by Stephen Ryan at Dataware. # (http://forums.intermapper.com/viewtopic.php?t=452) It is licenced # under a Creative Commons Attribution-ShareAlike 3.0 Unported # License, so we are free to use it as long as we attribute it to him. ipv6_regex = '^((([0-9A-Fa-f]{1,4}:){7}([0-9A-Fa-f]{1,4}|:))|(([0-9A-Fa-f]{1,4}:){6}(:[0-9A-Fa-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){5}(((:[0-9A-Fa-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9A-Fa-f]{1,4}:){4}(((:[0-9A-Fa-f]{1,4}){1,3})|((:[0-9A-Fa-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){3}(((:[0-9A-Fa-f]{1,4}){1,4})|((:[0-9A-Fa-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){2}(((:[0-9A-Fa-f]{1,4}){1,5})|((:[0-9A-Fa-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9A-Fa-f]{1,4}:){1}(((:[0-9A-Fa-f]{1,4}){1,6})|((:[0-9A-Fa-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9A-Fa-f]{1,4}){1,7})|((:[0-9A-Fa-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))(%.+)?$' hostname_is_ip_address = re.match(ipv4_regex, hostname) or re.match(ipv6_regex, hostname) headers = rhnFlags.get('outputTransportOptions') if len(token) < 5: # Bad auth information; decline any action log_debug(4, "incomplete proxy authentication token: %s" % auth_token) headers['X-RHN-Proxy-Auth-Error'] = '%s:%s' % ( 1003, _("incomplete proxy authentication token: %s") % auth_token) if not hostname_is_ip_address: headers['X-RHN-Proxy-Auth-Origin'] = hostname raise rhnFault(1003) # Invalid session key log_debug(5, "proxy auth token: %s, hostname: %s" % (repr(token), hostname or 'n/a')) proxyId, proxyUser, rhnServerTime, expireOffset, signature = token[:5] computed = computeSignature(CFG.SECRET_KEY, proxyId, proxyUser, rhnServerTime, expireOffset) if computed != signature: log_error("Proxy signature failed: proxy id='%s', proxy user='******'" % (proxyId, proxyUser)) log_debug(4, "Sent proxy signature %s does not match ours %s." % ( signature, computed)) headers['X-RHN-Proxy-Auth-Error'] = '%s:%s' % ( 1003, _("Sent proxy signature %s does not match ours %s.") % ( signature, computed)) if not hostname_is_ip_address: headers['X-RHN-Proxy-Auth-Origin'] = hostname raise rhnFault(1003) # Invalid session key # Convert the expiration/time to floats: rhnServerTime = float(rhnServerTime) expireOffset = float(expireOffset) if rhnServerTime + expireOffset < time.time(): log_debug(4, "Expired proxy authentication token") headers['X-RHN-Proxy-Auth-Error'] = '%s:%s' % (1004, "Expired") if not hostname_is_ip_address: headers['X-RHN-Proxy-Auth-Origin'] = hostname raise rhnFault(1004) # Expired client authentication token log_debug(4, "Proxy auth OK: sigs match; not an expired token") return 1
def authzOrg(self, info): # This function is a lot more complicated than it should be; the # corner case is pushes without a channel; we have to deny regular # users the ability to push to their org. # If the org id is not specified, default to the user's org id if not info.has_key("orgId"): info["orgId"] = self.org_id log_debug(4, "info[orgId]", info["orgId"], "org id", self.org_id) org_id = info["orgId"] if org_id == "": # Satellites are not allowwd to push in the null org raise rhnFault(4, _("You are not authorized to manage packages in the null org")) if org_id and self.org_id != org_id: # Not so fast... raise rhnFault(32, _("You are not allowed to manage packages in the %s org") % org_id) # Org admins and channel admins have full privileges; we could use # user_manages_channes, except for the case where there are no chanels if self.isOrgAdmin() or self.isChannelAdmin(): log_debug(4, "Org authorized (org_admin or channel_admin)") return # regular user at this point... check if the user manages any channels if user_manages_channels(self.user_id): log_debug(4, "Org authorized (user manages a channel)") return # ok, you're a regular user who doesn't manage any channels. # take a hike. raise rhnFault(32, _("You are not allowed to perform administrative tasks"))
def prepRhnCert(options): """ minor prepping of the RHN cerficate writing to default storage location """ # NOTE: default_db MUST be populated in /etc/rhn/rhn.conf before this # function is run. # validateSatCert() must have been run prior to this as well (it # populates "/var/log/entitlementCert" if options.rhn_cert and options.rhn_cert != DEFAULT_RHN_CERT_LOCATION: try: cert = openGzippedFile(options.rhn_cert).read() except (IOError, OSError), e: msg = _('ERROR: "%s" (specified in commandline)\n' "could not be opened and read:\n%s") % ( options.rhn_cert, str(e), ) sys.stderr.write(msg + "\n") raise cert = string.strip(cert) try: writeRhnCert(options, cert) except (IOError, OSError), e: msg = _('ERROR: "%s" could not be opened\nand/or written to:\n%s') % (DEFAULT_RHN_CERT_LOCATION, str(e)) sys.stderr.write(msg + "\n") raise
def getSourcePackagePath(self, pkgFilename): """ OVERLOADS getSourcePackagePath in common/rhnRepository. snag src.rpm and nosrc.rpm from local repo, after ensuring we are authorized to fetch it. """ log_debug(3, pkgFilename) if pkgFilename[-8:] != '.src.rpm' and pkgFilename[-10:] != '.nosrc.rpm': raise rhnFault(17, _("Invalid SRPM package requested: %s") % pkgFilename) # Connect to the server to get an authorization for downloading this # package server = rpclib.Server(self.rhnParentXMLRPC, proxy=self.httpProxy, username=self.httpProxyUsername, password=self.httpProxyPassword) if self.caChain: server.add_trusted_cert(self.caChain) try: retval = server.proxy.package_source_in_channel( pkgFilename, self.channelName, self.clientInfo) except xmlrpclib.Fault, e: raise rhnFault(1000, _("Error retrieving source package: %s") % str(e)), None, sys.exc_info()[2]
def exitWithTraceback(e, msg, exitnum, mail=0): tbOut = StringIO() Traceback(mail, ostream=tbOut, with_locals=1) log_error(-1, _('ERROR: %s %s: %s') % (e.__class__.__name__, msg, e)) log_error(-1, _('TRACEBACK: %s') % tbOut.getvalue()) sys.exit(exitnum)
def check_user_password(username, password): """ Do some minimal checks on the data thrown our way. """ # username is required if not username: raise rhnFault(11) # password is required if not password: raise rhnFault(12) if len(username) < CFG.MIN_USER_LEN: raise rhnFault(13, _("username should be at least %d characters") % CFG.MIN_USER_LEN) if len(username) > CFG.MAX_USER_LEN: raise rhnFault(700, _("username should be less than %d characters") % CFG.MAX_USER_LEN) username = username[:CFG.MAX_USER_LEN] # Invalid characters # ***NOTE*** Must coordinate with web and installer folks about any # changes to this set of characters!!!! invalid_re = re.compile(".*[\s&+%'`\"=#]", re.I) tmp = invalid_re.match(username) if tmp is not None: pos = tmp.regs[0] raise rhnFault(15, _("username = `%s', invalid character `%s'") % ( username, username[pos[1] - 1])) # use new password validation method validate_new_password(password) return username, password
def auth_client(self, token): """ Authenticate a system based on the same authentication tokens the client is sending for GET requests """ log_debug(3) # Build a UserDictCase out of the token dict = UserDictCase(token) # Set rhnFlags so that we can piggyback on apacheAuth's auth_client rhnFlags.set('AUTH_SESSION_TOKEN', dict) # XXX To clean up apacheAuth.auth_client's logging, this is not about # GET requests result = apacheAuth.auth_client() if not result: raise rhnFault(33, _("Invalid session key")) log_debug(4, "Client auth OK") # We checked it already, so we're sure it's there client_id = dict['X-RHN-Server-Id'] server = rhnServer.search(client_id) if not server: raise rhnFault(8, _("This server ID no longer exists")) # XXX: should we check if the username still has access to it? # probably not, because there is no known good way we can # update the server system_id on the client side when # permissions change... Damn it. --gafton self.server = server self.server_id = client_id self.user = dict['X-RHN-Auth-User-Id'] return server
def validate_system_input(self, data): """ check the input data """ if not hash_validate(data, "os_release", "architecture", "profile_name"): log_error("Incomplete data hash") raise rhnFault(21, _("Required data missing")) # we require either a username and a password or a token if not hash_validate(data, "username", "password") and \ not hash_validate(data, "token"): raise rhnFault(21, _("Required members missing"))
def __new_user_db(username, password, email, org_id, org_password): encrypted_password = CFG.encrypted_passwords log_debug(3, username, email, encrypted_password) # now search it in the database h = rhnSQL.prepare(""" select w.id, w.password, ui.use_pam_authentication from web_contact w, rhnUserInfo ui where w.login_uc = upper(:username) and w.id = ui.user_id """) h.execute(username=username) data = h.fetchone_dict() pre_existing_user = 0 if not data: # the username is not there, check the reserved user table h = rhnSQL.prepare(""" select login, password from rhnUserReserved where login_uc = upper(:username) """) h.execute(username=username) data = h.fetchone_dict() if not data: # nope, not reserved either raise rhnFault(1, _("Username `%s' has not been reserved") % username) else: pre_existing_user = 1 if not pre_existing_user and not email: # New accounts have to specify an e-mail address raise rhnFault(30, _("E-mail address not specified")) # we have to perform PAM authentication if data has a field called # 'use_pam_authentication' and its value is 'Y', and we do have a PAM # service set in the config file. # Note that if the user is only reserved we don't do PAM authentication if data.get('use_pam_authentication') == 'Y' and CFG.pam_auth_service: # Check the password with PAM import rhnAuthPAM if rhnAuthPAM.check_password(username, password, CFG.pam_auth_service) <= 0: # Bad password raise rhnFault(2) # We don't care about the password anymore, replace it with something import time password = '******' % time.time() else: # Regular authentication if check_password(password, data["password"]) == 0: # Bad password raise rhnFault(2) # creation of user was never supported in spacewalk but this call was mis-used # to check username/password in the past # so let's skip other checks and return now return 0
def auth_system(self): if CFG.DISABLE_ISS: raise rhnFault(2005, _('ISS is disabled on this satellite.')) if not rhnSQL.fetchone_dict("select 1 from rhnISSSlave where slave = :hostname and enabled = 'Y'", hostname=idn_puny_to_unicode(self.remote_hostname)): raise rhnFault(2004, _('Server "%s" is not enabled for ISS.') % self.remote_hostname) return self.remote_hostname
def auth_username_password(username, password): user = search(username) if not user: raise rhnFault(2, _("Invalid username/password combination")) if not user.check_password(password): raise rhnFault(2, _("Invalid username/password combination")) return user
def check_package_file(rel_path, logpkg, raisepkg): if rel_path is None: log_error("Package path null for package id", logpkg) raise rhnFault(17, _("Invalid RPM package %s requested") % raisepkg) filePath = "%s/%s" % (CFG.MOUNT_POINT, rel_path) if not os.access(filePath, os.R_OK): # Package not found on the filesystem log_error("Package not found", filePath) raise rhnFault(17, _("Package not found")) return filePath
def auth_username_password(username, password): # hrm. it'd be nice to move importlib.userAuth stuff here user = search(username) if not user: raise rhnFault(2, _("Invalid username/password combination")) if not user.check_password(password): raise rhnFault(2, _("Invalid username/password combination")) return user
def header(self, system_id, pkgList): """ Clients v1- IN: system_id: .... a package identifier (or a list of them) [n,v,r,e] or [[n,v,r,e],...] OUT: If Proxy: If Client: """ log_debug(5, system_id, pkgList) if type(pkgList) not in (ListType, TupleType) or not len(pkgList): log_error("Invalid package list spec", type(pkgList), "len = %d" % len(pkgList)) raise rhnFault(30, _("Invalid value %s (type %s)") % ( pkgList, type(pkgList))) # Okay, it's a list; is it a list of lists? if type(pkgList[0]) is StringType: # Wrap it in a list pkgList = [pkgList] # Now check all params req_list = [] for p in pkgList: req_list.append(check_package_spec(p)) # Authenticate the system certificate server = self.auth_system('header', system_id) # log the entry log_debug(1, self.server_id, "items: %d" % len(req_list)) rpmHeaders = [] for pkg in pkgList: # Authorize this package fetch. # XXX: a bit heavy-handed I think... but old client stuff. # NOTE: pkg for old client is [n,v,r,e] path = rhnPackage.get_package_path_compat_arches( self.server_id, pkg, server.archname) # read the header from the file on disk h = rhn_rpm.get_package_header(filename=path) if h is None: log_error("Unable to read package header", pkg) raise rhnFault(17, _("Unable to retrieve package header %s") % str(pkg)) rpmHeaders.append(rpclib.xmlrpclib.Binary(h.unload())) del h # Reset the flag for the proxy download accelerator # This gets set by default in rhnPackage rhnFlags.set("Download-Accelerator-Path", None) if CFG.COMPRESS_HEADERS: # Compress rhnFlags.set("compress_response", 1) return rpmHeaders
def call_function(self, method, params): # short-circuit everything if sending a system-wide message. if CFG.SEND_MESSAGE_TO_ALL: # Make sure the applet doesn't see the message if method == 'applet.poll_status': return self.response({ 'checkin_interval' : 3600, 'server_status' : 'normal' }) if method == 'applet.poll_packages': return self.response({ 'use_cached_copy' : 1 }) # Fetch global message being sent to clients if applicable. msg = open(CFG.MESSAGE_TO_ALL).read() log_debug(3, "Sending message to all clients: %s" % msg) # Send the message as a fault. response = rpclib.Fault( -1, _("IMPORTANT MESSAGE FOLLOWS:\n%s") % msg) # and now send everything back ret = self.response(response) log_debug(4, "Leave with return value", ret) return ret # req: where the response is sent to log_debug(2, method) # Now we have the reference, call away force_rollback = 1 try: # now get the function reference and call it func = self.method_ref(method) if len(params): response = apply(func, params) else: response = func() except (TypeError, ValueError, KeyError, IndexError, UnknownXML): # report exception back to server fault = 1 if sys.exc_type == UnknownXML: fault = -1 e_type, e_value = sys.exc_info()[:2] response = rpclib.Fault(fault, _( "While running '%s': caught\n%s : %s\n") % ( method, e_type, e_value)) Traceback(method, self.req, extra="Response sent back to the caller:\n%s\n" % ( response.faultString,), severity="notification") #pkilambi:catch exception if redirect except redirectException, re: log_debug(3,"redirect exception caught",re.path) response = re.path
def validate_new_username(username): """ Perform all the checks required for new usernames. """ log_debug(3) if len(username) < CFG.MIN_NEW_USER_LEN: raise rhnFault(13, _("username should be at least %d characters long") % CFG.MIN_NEW_USER_LEN) disallowed_suffixes = CFG.DISALLOWED_SUFFIXES or [] if not isinstance(disallowed_suffixes, type([])): disallowed_suffixes = [disallowed_suffixes] log_debug(4, "Disallowed suffixes", disallowed_suffixes) for suffix in disallowed_suffixes: if username[-len(suffix) :].upper() == suffix.upper(): raise rhnFault(106, _("Cannot register usernames ending with %s") % suffix)
def auth_system(self, system_id): """ System authentication. We override the standard function because we need to check additionally if this system_id is entitled for proxy functionality. """ log_debug(3) server = rhnHandler.auth_system(self, system_id) # if it did not blow up, we have a valid server. Check proxy # entitlement. # XXX: this needs to be moved out of the rhnServer module, # possibly in here h = rhnSQL.prepare(""" select 1 from rhnProxyInfo pi where pi.server_id = :server_id """) h.execute(server_id = self.server_id) row = h.fetchone_dict() if not row: # we require entitlement for this functionality log_error("Server not entitled for Proxy", self.server_id) raise rhnFault(1002, _( 'Spacewalk Proxy service not enabled for server profile: "%s"') % server.server["name"]) # we're fine... return server
def _connectToParent(self): """ Handler part 1 Should not return an error code -- simply connects. """ scheme, host, port, self.uri = self._parse_url(self.rhnParent) self.responseContext.setConnection(self._create_connection()) if not self.uri: self.uri = '/' log_debug(3, 'Scheme:', scheme) log_debug(3, 'Host:', host) log_debug(3, 'Port:', port) log_debug(3, 'URI:', self.uri) log_debug(3, 'HTTP proxy:', self.httpProxy) log_debug(3, 'HTTP proxy username:'******'HTTP proxy password:'******'CA cert:', self.caChain) try: self.responseContext.getConnection().connect() except socket.error, e: log_error("Error opening connection", self.rhnParent, e) Traceback(mail=0) raise rhnFault(1000, _("RHN Proxy could not successfully connect its RHN parent. " "Please contact your system administrator."))
def getPackageErratum(self, system_id, pkg): """ Clients v2+ - Get errata for a package given [n,v,r,e,a,...] format Sing-along: You say erratum(sing), I say errata(pl)! :) IN: pkg: [n,v,r,e,s,a,ch,...] RET: a hash by errata that applies to this package """ log_debug(5, system_id, pkg) if type(pkg) != type([]) or len(pkg) < 7: log_error("Got invalid package specification: %s" % str(pkg)) raise rhnFault(30, _("Expected a package, not: %s") % pkg) # Authenticate and decode server id. self.auth_system(system_id) # log the entry log_debug(1, self.server_id, pkg) # Stuff the action in the headers: transport = rhnFlags.get("outputTransportOptions") transport["X-RHN-Action"] = "getPackageErratum" name, ver, rel, epoch, arch, size, channel = pkg[:7] if epoch in ["", "none", "None"]: epoch = None # XXX: also, should arch/size/channel ever be used? # bug#186996:adding synopsis field to errata info # client side changes are needed to access this data. h = rhnSQL.prepare( """ select distinct e.id errata_id, e.advisory_type errata_type, e.advisory advisory, e.topic topic, e.description description, e.synopsis synopsis from rhnServerChannel sc, rhnChannelPackage cp, rhnChannelErrata ce, rhnErrata e, rhnErrataPackage ep, rhnPackage p where p.name_id = LOOKUP_PACKAGE_NAME(:name) and p.evr_id = LOOKUP_EVR(:epoch, :ver, :rel) -- map to a channel and p.id = cp.package_id -- map to an errata as well and p.id = ep.package_id and ep.errata_id = e.id -- the errata and the channel have to be linked and e.id = ce.errata_id and ce.channel_id = cp.channel_id -- and the server has to be subscribed to the channel and cp.channel_id = sc.channel_id and sc.server_id = :server_id """ ) # " emacs sucks h.execute(name=name, ver=ver, rel=rel, epoch=epoch, server_id=str(self.server_id)) return self._sanitize_result(h)
def authzChannels(self, channels): log_debug(4, channels) if not channels: return # rhn_channel.user_role_check checks for the ownership of the channel # by this user's org h = rhnSQL.prepare(""" select rhn_channel.user_role_check(id, :user_id, 'manage') manage from rhnChannel where label = :channel """) for channel in channels: h.execute(channel=channel, user_id=self.user_id) row = h.fetchone_dict() # Either the channel doesn't exist, or not allowed to manage it if not row or not row['manage']: raise rhnFault(32, _("You are not allowed to manage channel %s, or that " "channel does not exist") % channel) log_debug(4, "User %s allowed to manage channel %s" % (self.user_id, channel)) return None
def get_package_path_by_filename(self, fileName, channel): log_debug(3, fileName, channel) fileName = str(fileName) n, e, v, r, a = rhnLib.parseRPMFilename(fileName) h = rhnSQL.prepare(self._query_get_package_path_by_nvra) h.execute(name=n, version=v, release=r, epoch=e, arch=a, channel=channel) try: return _get_path_from_cursor(h) except InvalidPackageError: log_debug(4, "Error", "Non-existent package requested", fileName) raise rhnFault(17, _("Invalid RPM package %s requested") % fileName), None, sys.exc_info()[2] except NullPathPackageError, e: package_id = e[0] log_error("Package path null for package id", package_id) raise rhnFault(17, _("Invalid RPM package %s requested") % fileName), None, sys.exc_info()[2]
def auth_proxy(): """ Authenticates a proxy carrying a clients request. For a valid or unsigned request, this function returns 1 (OK), otherwise it raises rhnFault NOTE: X-RHN-Proxy-Auth described in proxy/broker/rhnProxyAuth.py """ log_debug(3) headers = rhnFlags.get('outputTransportOptions') if not rhnFlags.test('X-RHN-Proxy-Auth'): # No auth information; decline any action log_debug(4, "declined proxy authentication") headers['X-RHN-Proxy-Auth-Error'] = '%s:%s' % ( 1003, _("declined proxy authentication")) raise rhnFault(1003) # Invalid session key # NOTE: # - < v3.1 RHN proxies send only 1 token in this header # - > v3.1: we send the route of the requests via multiple tokens # "token1:hostname1,token2:hostname2" the first tuple is the first # proxy hit. tokens = string.split(rhnFlags.get('X-RHN-Proxy-Auth'), ',') tokens = filter(lambda token: token, tokens) for auth_token in tokens: _verifyProxyAuthToken(auth_token) # if no rhnFault was raised then the tokens all passed return 1
def getPackagePath(self, pkgFilename): """ OVERLOADS getPackagePath in common/rhnRepository. Returns complete path to an RPM file. """ log_debug(3, pkgFilename) mappingName = "package_mapping:%s:" % self.channelName pickledMapping = self._cacheObj(mappingName, self.channelVersion, self.__channelPackageMapping, ()) mapping = cPickle.loads(pickledMapping) # If the file name has parameters, it's a different kind of package. # Determine the architecture requested so we can construct an # appropriate filename. if type(pkgFilename) == types.ListType: arch = pkgFilename[3] if isSolarisArch(arch): pkgFilename = "%s-%s-%s.%s.pkg" % (pkgFilename[0], pkgFilename[1], pkgFilename[2], pkgFilename[3]) if not mapping.has_key(pkgFilename): log_error("Package not in mapping: %s" % pkgFilename) raise rhnFault(17, _("Invalid RPM package requested: %s") % pkgFilename) filePath = "%s/%s" % (CFG.PKG_DIR, mapping[pkgFilename]) log_debug(4, "File path", filePath) if not os.access(filePath, os.R_OK): log_debug(4, "Package not found locally: %s" % pkgFilename) raise NotLocalError(filePath, pkgFilename) return filePath
def entitle(self, server_id, history, virt_type=None): """ Entitle a server according to the entitlements we have configured. """ log_debug(3, self.entitlements) entitle_server = rhnSQL.Procedure("rhn_entitlements.entitle_server") # TODO: entitle_server calls can_entitle_server, so we're doing this # twice for each successful call. Is it necessary for external error # handling or can we ditch it? can_entitle_server = rhnSQL.Function( "rhn_entitlements.can_entitle_server", rhnSQL.types.NUMBER()) can_ent = None history["entitlement"] = "" # Do a quick check to see if both virt entitlements are present. (i.e. # activation keys stacked together) If so, give preference to the more # powerful virtualization platform and remove the regular virt # entitlement from the list. found_virt = False found_virt_platform = False for entitlement in self.entitlements: if entitlement[0] == VIRT_ENT_LABEL: found_virt = True elif entitlement[0] == VIRT_PLATFORM_ENT_LABEL: found_virt_platform = True for entitlement in self.entitlements: if virt_type is not None and entitlement[0] in \ (VIRT_ENT_LABEL, VIRT_PLATFORM_ENT_LABEL): continue # If both virt entitlements are present, skip the least powerful: if found_virt and found_virt_platform and entitlement[0] == VIRT_ENT_LABEL: log_debug(1, "Virtualization and Virtualization Platform " + "entitlements both present.") log_debug(1, "Skipping Virtualization.") continue try: can_ent = can_entitle_server(server_id, entitlement[0]) except rhnSQL.SQLSchemaError, e: can_ent = 0 try: # bugzilla #160077, skip attempting to entitle if we cant if can_ent: entitle_server(server_id, entitlement[0]) except rhnSQL.SQLSchemaError, e: log_error("Token failed to entitle server", server_id, self.get_names(), entitlement[0], e.errmsg) if e.errno == 20220: # ORA-20220: (servergroup_max_members) - Server group membership # cannot exceed maximum membership raise rhnFault(91, _("Registration failed: RHN Software service entitlements exhausted: %s") % entitlement[0]), None, sys.exc_info()[2] # No idea what error may be here... raise rhnFault(90, e.errmsg), None, sys.exc_info()[2]
def token_server_groups(server_id, tokens_obj): """ Handle server group subscriptions for the registration token """ assert(isinstance(tokens_obj, ActivationTokens)) h = rhnSQL.prepare(_query_token_server_groups) server_groups = {} for token in tokens_obj.tokens: token_id = token['token_id'] h.execute(token_id=token_id) while 1: row = h.fetchone_dict() if not row: break server_group_id = row['server_group_id'] server_groups[server_group_id] = row # Now try to subscribe server to group ret = [] for server_group_id, sg in server_groups.items(): log_debug(4, "token server group", sg) try: join_server_group(server_id, server_group_id) except rhnSQL.SQLError, e: log_error("Failed to add server to group", server_id, server_group_id, sg["name"]) raise rhnFault(80, _("Failed to add server to group %s") % sg["name"]), None, sys.exc_info()[2] else: ret.append("Subscribed to server group '%s'" % sg["name"])
def __getXmlrpcServer(): """ get an xmlrpc server object WARNING: if CFG.USE_SSL is off, we are sending info in the clear. """ log_debug(3) # build the URL url = CFG.RHN_PARENT or '' url = parseUrl(url)[1].split(':')[0] if CFG.USE_SSL: url = 'https://' + url + '/XMLRPC' else: url = 'http://' + url + '/XMLRPC' log_debug(3, 'server url: %s' % url) if CFG.HTTP_PROXY: serverObj = rpclib.Server(url, proxy=CFG.HTTP_PROXY, username=CFG.HTTP_PROXY_USERNAME, password=CFG.HTTP_PROXY_PASSWORD) else: serverObj = rpclib.Server(url) if CFG.USE_SSL and CFG.CA_CHAIN: if not os.access(CFG.CA_CHAIN, os.R_OK): log_error('ERROR: missing or cannot access (for ca_chain): %s' % CFG.CA_CHAIN) raise rhnFault(1000, _("Spacewalk Proxy error (file access issues). " "Please contact your system administrator. " "Please refer to Spacewalk Proxy logs.")) serverObj.add_trusted_cert(CFG.CA_CHAIN) serverObj.add_header('X-RHN-Client-Version', 2) return serverObj
def GetByPackage(self, pkg, osRel): """ Clients v1- Get errata for a package given "n-v-r" format IN: pkg: "n-v-r" (old client call) or [n,v,r] osRel: OS release RET: a hash by errata that applies to this package (ie, newer packages are available). We also limit the scope for a particular osRel. """ if type(pkg) == type(''): # Old client support. pkg = parseRPMName(pkg) log_debug(1, pkg, osRel) # Stuff the action in the headers: transport = rhnFlags.get('outputTransportOptions') transport['X-RHN-Action'] = 'GetByPackage' # now look up the errata if type(pkg[0]) != type(''): log_error("Invalid package name: %s %s" % (type(pkg[0]), pkg[0])) raise rhnFault(30, _("Expected a package name, not: %s") % pkg[0]) # bug#186996:adding synopsis field to advisory info # client side changes are needed to access this data. h = rhnSQL.prepare(""" select distinct e.id errata_id, e.advisory_type errata_type, e.advisory advisory, e.topic topic, e.description description, e.synopsis synopsis from rhnErrata e, rhnPublicChannelFamily pcf, rhnChannelFamilyMembers cfm, rhnErrataPackage ep, rhnChannelPackage cp, rhnChannelErrata ce, rhnDistChannelMap dcm, rhnPackage p where 1=1 and p.name_id = LOOKUP_PACKAGE_NAME(:name) -- map to a channel and p.id = cp.package_id and cp.channel_id = dcm.channel_id and dcm.release = :dist and dcm.org_id is null -- map to an errata as well and p.id = ep.package_id and ep.errata_id = e.id -- the errata and the channel have to be linked and ce.channel_id = cp.channel_id -- and the channel has to be public and cp.channel_id = cfm.channel_id and cfm.channel_family_id = pcf.channel_family_id -- and get the erratum and e.id = ce.errata_id """) h.execute(name=pkg[0], dist=str(osRel)) return self._sanitize_result(h)
def set_arch(self, arch): self.archname = arch # try to detect the archid if arch is None: return arch = rhnLib.normalize_server_arch(arch) h = rhnSQL.prepare(self._query_lookup_arch) h.execute(archname=arch) data = h.fetchone_dict() if not data: # Log it to disk, it may show interesting things log_error("Attempt to create server with invalid arch `%s'" % arch) raise rhnFault(24, _("Architecture `%s' is not supported") % arch) self.server["server_arch_id"] = data["id"] self.is_rpm_managed = data['is_rpm_managed']
def check_email(email): """ Do some minimal checks on the e-mail address """ if email is not None: email = email.strip() if not email: # Still supported return None if len(email) > CFG.MAX_EMAIL_LEN: raise rhnFault( 100, _("Please limit your e-mail address to %s chars") % CFG.MAX_EMAIL_LEN) # XXX More to come (check the format is indeed [email protected] return email
def has_base_channel(self, uuid): log_debug(1, uuid) # Verifies if a system has a base channel h = rhnSQL.prepare(self._query_lookup_server) h.execute(uuid=uuid) row = h.fetchone_dict() if not row: raise rhnFault(140, _("Your system was not found in the RHN database"), explain=0) server_id = row['id'] h = rhnSQL.prepare(self._query_lookup_base_channel) h.execute(server_id=server_id) row = h.fetchone_dict() if row: return 1 return 0
def _validate_entitlements(token_string, rereg_ents, base_entitlements, extra_entitlements, remove_entitlements): """ Perform various checks on the final list of entitlements accumulated after processing all activation keys. rereg_ents passed in as a list of entitlement labels. Extra/base entitlements passed in as a hash of tuples ('label', 'Friendly Name') mapping to None. (i.e. seems to be used as just a set) Remove entitlements being maintained as just a list of labels. """ # Check for exactly one base entitlement: if len(base_entitlements.keys()) != 1: log_error("Tokens with different base entitlements", token_string, base_entitlements) raise rhnFault( 63, _("Stacking of re-registration tokens with different base entitlements " "is not supported"), explain=0) # Don't allow an activation key to give virt entitlement to a system # that's re-activating and already has virt platform: (or vice-versa) found_virt = False virt_tuple = None found_virt_platform = False for ent_tuple in extra_entitlements.keys(): if ent_tuple[0] == VIRT_ENT_LABEL: found_virt = True virt_tuple = ent_tuple elif ent_tuple[0] == VIRT_PLATFORM_ENT_LABEL: found_virt_platform = True if found_virt and found_virt_platform and len(rereg_ents) > 0: # Both virt entitlements found, give preference to the most powerful. # (i.e. virtualization_host_platform) This may mean we have to remove # virtualization_host if a reregistration key is in use and contains # this entitlement. if VIRT_ENT_LABEL in rereg_ents: # The system already has virt host, so it must be removed: log_debug(1, "Removing Virtualization entitlement from profile.") remove_entitlements.append(virt_tuple[0])
def __request(self, methodname, params): # pylint: disable=R0915 log_debug(6, methodname, params) # Init the socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect(self.serverAddr) except socket.error, e: sock.close() methodname = None log_error("Error connecting to the auth cache: %s" % str(e)) Traceback("Shelf.__request", extra=""" Error connecting to the the authentication cache daemon. Make sure it is started on %s""" % str(self.serverAddr)) # FIXME: PROBLEM: this rhnFault will never reach the client raise rhnFault(1000, _("Spacewalk Proxy error (issues connecting to auth cache). " "Please contact your system administrator")), None, sys.exc_info()[2]
def headerParserHandler(self, req): # pylint: disable=W0201 log_setreq(req) self.start_time = time.time() # init configuration options with proper component options = req.get_options() # if we are initializing out of a <Location> handler don't # freak out if "RHNComponentType" not in options: # clearly nothing to do return apache.OK initCFG(options["RHNComponentType"]) initLOG(CFG.LOG_FILE, CFG.DEBUG) # short-circuit everything if sending a system-wide message. if CFG.SEND_MESSAGE_TO_ALL: # Drop the database connection # pylint: disable=W0702 try: rhnSQL.closeDB() except: pass # Fetch global message being sent to clients if applicable. msg = open(CFG.MESSAGE_TO_ALL).read() log_debug(3, "Sending message to all clients: %s" % msg) return self._send_xmlrpc( req, rhnFault(-1, _("IMPORTANT MESSAGE FOLLOWS:\n%s") % msg, explain=0)) rhnSQL.initDB() self.server = options['SERVER'] self.server_classes = rhnImport.load("satellite_exporter/handlers") if self.server not in self.server_classes: # XXX do something interesting here log_error("Missing server", self.server) return apache.HTTP_NOT_FOUND return self._wrapper(req, self._headerParserHandler)
def _check_token_limits(server_id, token_rec): token_id = token_rec["token_id"] # Mark that we used this token server_used_token(server_id, token_id) # now check we're not using this token too much h = rhnSQL.prepare(_query_check_token_limits) h.execute(token_id=token_id) ret = h.fetchone_dict() if not ret: raise rhnException("Could not check usage limits for token", server_id, token_rec) # See bug #79095: if usage_limit is NULL, it means unlimited reg tokens if ret["max_nr"] is not None and ret["max_nr"] < ret["curr_nr"]: log_error("Token usage limit exceeded", token_rec, ret["max_nr"], server_id) raise rhnFault(61, _("Maximum usage count of %s reached") % ret["max_nr"]) # all clean, we're below usage limits return 0
def _validate_entitlements(token_string, rereg_ents, base_entitlements, extra_entitlements, remove_entitlements): """ Perform various checks on the final list of entitlements accumulated after processing all activation keys. rereg_ents passed in as a list of entitlement labels. Extra/base entitlements passed in as a hash of tuples ('label', 'Friendly Name') mapping to None. (i.e. seems to be used as just a set) Remove entitlements being maintained as just a list of labels. """ # Check for exactly one base entitlement: if len(list(base_entitlements.keys())) != 1: log_error("Tokens with different base entitlements", token_string, base_entitlements) raise rhnFault(63, _("Stacking of re-registration tokens with different base entitlements " "is not supported"), explain=0)
def getPackagePath(self, pkgFilename, redirect=0): """ OVERLOADS getPackagePath in common/rhnRepository. Returns complete path to an RPM file. """ log_debug(3, pkgFilename) mappingName = "package_mapping:%s:" % self.channelName pickledMapping = self._cacheObj(mappingName, self.channelVersion, self.__channelPackageMapping, ()) mapping = cPickle.loads(pickledMapping) # If the file name has parameters, it's a different kind of package. # Determine the architecture requested so we can construct an # appropriate filename. if type(pkgFilename) == types.ListType: arch = pkgFilename[3] if isSolarisArch(arch): pkgFilename = "%s-%s-%s.%s.pkg" % \ (pkgFilename[0], pkgFilename[1], pkgFilename[2], pkgFilename[3]) if not mapping.has_key(pkgFilename): log_error("Package not in mapping: %s" % pkgFilename) raise rhnFault( 17, _("Invalid RPM package requested: %s") % pkgFilename) # A list of possible file paths. Always a list, channel mappings are # cleared on package upgrade so we don't have to worry about the old # behavior of returning a string filePaths = mapping[pkgFilename] # Can we see a file at any of the possible filepaths? for filePath in filePaths: filePath = "%s/%s" % (CFG.PKG_DIR, filePath) log_debug(4, "File path", filePath) if os.access(filePath, os.R_OK): return filePath log_debug(4, "Package not found locally: %s" % pkgFilename) raise NotLocalError(filePaths[0], pkgFilename)
def _connectToParent(self): """ Handler part 1 Should not return an error code -- simply connects. """ scheme, host, port, self.uri, query = self._parse_url(self.rhnParent) self.responseContext.setConnection(self._create_connection()) if not self.uri: self.uri = '/' # if this request is for an upstream server, use the original query string. # Otherwise, if it is for the local Squid instance, strip it so that # Squid will not keep multiple cached copies of the same resource if self.httpProxy not in ['127.0.0.1:8080', 'localhost:8080']: if 'X-Suse-Auth-Token' in self.req.headers_in: self.uri += '?%s' % self.req.headers_in['X-Suse-Auth-Token'] elif query: self.uri += '?%s' % query log_debug(3, 'Scheme:', scheme) log_debug(3, 'Host:', host) log_debug(3, 'Port:', port) log_debug(3, 'URI:', self.uri) log_debug(3, 'HTTP proxy:', self.httpProxy) log_debug(3, 'HTTP proxy username:'******'HTTP proxy password:'******'CA cert:', self.caChain) try: self.responseContext.getConnection().connect() except socket.error, e: log_error("Error opening connection", self.rhnParent, e) Traceback(mail=0) raise rhnFault( 1000, _("SUSE Manager Proxy could not successfully connect its SUSE Manager parent. " "Please contact your system administrator.") ), None, sys.exc_info()[2]
def __solveDep_prepare(self, system_id, deps, action, clientVersion): """ Response for clients: version 1: list version 2: hash """ log_debug(7, system_id, deps, action, clientVersion) faultString = _("Invalid value %s (%s)") if type(deps) not in (ListType, TupleType): log_error("Invalid argument type", type(deps)) raise rhnFault(30, faultString % (deps, type(deps))) for dep in deps: if type(dep) is not StringType: log_error("Invalid dependency member", type(dep)) raise rhnFault(30, faultString % (dep, type(dep))) # Ignore empty strings deps = list(filter(len, deps)) # anything left to do? if not deps: return [] # Authenticate the system certificate server = self.auth_system(action, system_id) log_debug(1, self.server_id, action, "items: %d" % len(deps)) return deps
def getUserGroups(login, password): # Authenticates a user and returns the list of groups it belongs # to, and the org id add_to_seclist(password) log_debug(4, login) user = rhnUser.search(login) if not user: log_debug("rhnUser.search failed") raise rhnFault(2) # Check the user's password if not user.check_password(password): log_debug("user.check_password failed") raise rhnFault(2) if rhnUser.is_user_disabled(username): msg = _(""" %s Account has been deactivated on this server. Please contact your Org administrator for more help.""") raise rhnFault(1, msg % username, explain=0) return getUserGroupsFromUserInstance(user)
def register_product(self, system_id, product, oeminfo = {}): """ register a product and record the data sent with the registration bretm: hasn't registered a product or recorded anything since 2001, near as I can tell what it actually appears to be responsible for is protecting us against people registering systems from t7/t9 countries actual use of registration numbers has been moved into the server_class.__save stuff """ log_debug(5, system_id, product, oeminfo) if type(product) != type({}): log_error("Invalid argument type", type(product)) raise rhnFault(21, _( "Expected a dictionary as a product argument")) log_debug(4, product) # As per bug 129996 using an activation key should not overwrite the # user's info. Also, the reg number stuff doesn't work anyway # Keep doing the authentication and then just bail out self.auth_system(system_id) return 0
def __getXmlrpcServer(): """ get an xmlrpc server object WARNING: if CFG.USE_SSL is off, we are sending info in the clear. """ log_debug(3) # build the URL url = CFG.RHN_PARENT or '' url = parseUrl(url)[1].split(':')[0] if CFG.USE_SSL: url = 'https://' + url + '/XMLRPC' else: url = 'http://' + url + '/XMLRPC' log_debug(3, 'server url: %s' % url) if CFG.HTTP_PROXY: serverObj = rpclib.Server(url, proxy=CFG.HTTP_PROXY, username=CFG.HTTP_PROXY_USERNAME, password=CFG.HTTP_PROXY_PASSWORD) else: serverObj = rpclib.Server(url) if CFG.USE_SSL and CFG.CA_CHAIN: if not os.access(CFG.CA_CHAIN, os.R_OK): log_error( 'ERROR: missing or cannot access (for ca_chain): %s' % CFG.CA_CHAIN) raise rhnFault( 1000, _("SUSE Manager Proxy error (file access issues). " "Please contact your system administrator. " "Please refer to SUSE Manager Proxy logs.")) serverObj.add_trusted_cert(CFG.CA_CHAIN) serverObj.add_header('X-RHN-Client-Version', 2) return serverObj
def call_function(self, method, params): # short-circuit everything if sending a system-wide message. if CFG.SEND_MESSAGE_TO_ALL: # Make sure the applet doesn't see the message if method == 'applet.poll_status': return self.response({ 'checkin_interval': 3600, 'server_status': 'normal' }) if method == 'applet.poll_packages': return self.response({'use_cached_copy': 1}) # Fetch global message being sent to clients if applicable. msg = open(CFG.MESSAGE_TO_ALL).read() log_debug(3, "Sending message to all clients: %s" % msg) # Send the message as a fault. response = xmlrpclib.Fault( -1, _("IMPORTANT MESSAGE FOLLOWS:\n%s") % msg) # and now send everything back ret = self.response(response) log_debug(4, "Leave with return value", ret) return ret # req: where the response is sent to log_debug(2, method) # Now we have the reference, call away force_rollback = 1 try: rhnSQL.clear_log_id() # now get the function reference and call it func = self.method_ref(method) response = func(*params) except (TypeError, ValueError, KeyError, IndexError, UnknownXML): # report exception back to server fault = 1 if sys.version_info[0] == 3: exctype = sys.exc_info()[0] else: exctype = sys.exc_type if exctype == UnknownXML: fault = -1 e_type, e_value = sys.exc_info()[:2] response = xmlrpclib.Fault(fault, _( "While running '%s': caught\n%s : %s\n") % ( method, e_type, e_value)) Traceback(method, self.req, extra="Response sent back to the caller:\n%s\n" % ( response.faultString,), severity="notification") except rhnNotFound: e = sys.exc_info()[1] return apache.HTTP_NOT_FOUND # pkilambi:catch exception if redirect except redirectException: re = sys.exc_info()[1] log_debug(3, "redirect exception caught", re.path) response = re.path except rhnFault: f = sys.exc_info()[1] response = f.getxml() except rhnSQL.SQLSchemaError: e = sys.exc_info()[1] f = None if e.errno == 20200: log_debug(2, "User Group Membership EXCEEDED") f = rhnFault(43, e.errmsg) if not f: log_error("rhnSQL.SQLSchemaError caught", e) rhnSQL.rollback() # generate the traceback report Traceback(method, self.req, extra="SQL Error generated: %s" % e, severity="schema") return apache.HTTP_INTERNAL_SERVER_ERROR response = f.getxml() except rhnSQL.SQLError: e = sys.exc_info()[1] log_error("rhnSQL.SQLError caught", e) rhnSQL.rollback() Traceback(method, self.req, extra="SQL Error generated: %s" % e, severity="schema") return apache.HTTP_INTERNAL_SERVER_ERROR except Exception: e = sys.exc_info()[1] log_error("Unhandled exception", e) rhnSQL.rollback() # otherwise we do a full stop Traceback(method, self.req, severity="unhandled") return apache.HTTP_INTERNAL_SERVER_ERROR else: # if no exception, we don't need to rollback force_rollback = 0 if force_rollback: rhnSQL.rollback() rhnSQL.clear_log_id() # and now send everything back ret = self.response(response) log_debug(4, "Leave with return value", ret) return ret
def __request(self, methodname, params): # pylint: disable=R0915 log_debug(6, methodname, params) # Init the socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect(self.serverAddr) except socket.error as e: sock.close() methodname = None log_error("Error connecting to the auth cache: %s" % str(e)) Traceback("Shelf.__request", extra=""" Error connecting to the the authentication cache daemon. Make sure it is started on %s""" % str(self.serverAddr)) # FIXME: PROBLEM: this rhnFault will never reach the client raise_with_tb( rhnFault( 1000, _("Spacewalk Proxy error (issues connecting to auth cache). " "Please contact your system administrator")), sys.exc_info()[2]) wfile = sock.makefile("w") try: send(wfile, methodname, None, *params) except CommunicationError: wfile.close() sock.close() Traceback("Shelf.__request", extra="Encountered a CommunicationError") raise except socket.error: wfile.close() sock.close() log_error("Error communicating to the auth cache: %s" % str(e)) Traceback("Shelf.__request", extra="""\ Error sending to the authentication cache daemon. Make sure the authentication cache daemon is started""") # FIXME: PROBLEM: this rhnFault will never reach the client raise_with_tb( rhnFault( 1000, _("Spacewalk Proxy error (issues connecting to auth cache). " "Please contact your system administrator")), sys.exc_info()[2]) wfile.close() rfile = sock.makefile("r") try: params, methodname = recv(rfile) except CommunicationError as e: log_error(e.faultString) rfile.close() sock.close() log_error("Error communicating to the auth cache: %s" % str(e)) Traceback("Shelf.__request", extra="""\ Error receiving from the authentication cache daemon. Make sure the authentication cache daemon is started""") # FIXME: PROBLEM: this rhnFault will never reach the client raise_with_tb( rhnFault( 1000, _("Spacewalk Proxy error (issues communicating to auth cache). " "Please contact your system administrator")), sys.exc_info()[2]) except Fault as e: rfile.close() sock.close() # If e.faultCode is 0, it's another exception if e.faultCode != 0: # Treat is as a regular xmlrpc fault raise _dict = e.faultString if not isinstance(_dict, type({})): # Not the expected type raise if 'name' not in _dict: # Doesn't look like a marshalled exception raise name = _dict['name'] args = _dict.get('args') # Look up the exception if not hasattr(__builtins__, name): # Unknown exception name raise # Instantiate the exception object import new _dict = {'args': args} # pylint: disable=bad-option-value,nonstandard-exception raise_with_tb(new.instance(getattr(__builtins__, name), _dict), sys.exc_info()[2]) return params[0]
def login(self): """ Login and fetch new token (proxy token). How it works in a nutshell. Only the broker component uses this. We perform a xmlrpc request to rhn_parent. This occurs outside of the http process we are currently working on. So, we do this all on our own; do all of our own SSL decisionmaking etc. We use CFG.RHN_PARENT as we always bypass the SSL redirect. DESIGN NOTES: what is the proxy auth token? ------------------------------------------- An SUSE Manager Proxy auth token is a token fetched upon login from SUSE Manager Server or hosted. It has this format: 'S:U:ST:EO:SIG' Where: S = server ID U = username ST = server time EO = expiration offset SIG = signature H = hostname (important later) Within this function within the SUSE Manager Proxy Broker we also tag on the hostname to the end of the token. The token as described above is enough for authentication purposes, but we need a to identify the exact hostname (as the SUSE Manager Proxy sees it). So now the token becomes (token:hostname): 'S:U:ST:EO:SIG:H' DESIGN NOTES: what is X-RHN-Proxy-Auth? ------------------------------------------- This is where we use the auth token beyond SUSE Manager Proxy login purposes. This a header used to track request routes through a hierarchy of SUSE Manager Proxies. X-RHN-Proxy-Auth is a header that passes proxy authentication information around in the form of an ordered list of tokens. This list is used to gain information as to how a client request is routed throughout an RHN topology. Format: 'S1:U1:ST1:EO1:SIG1:H1,S2:U2:ST2:EO2:SIG2:H2,...' |_________1_________| |_________2_________| |__... token token where token is really: token:hostname leftmost token was the first token hit by a client request. rightmost token was the last token hit by a client request. """ # pylint: disable=R0915 log_debug(3) server = self.__getXmlrpcServer() error = None token = None # update the systemid/serverid if need be. self.__processSystemid() # Makes three attempts to login for _i in range(self.__nRetries): try: token = server.proxy.login(self.__systemid) except (socket.error, socket_error) as e: if CFG.HTTP_PROXY: # socket error, check to see if your HTTP proxy is running... s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) httpProxy, httpProxyPort = CFG.HTTP_PROXY.split(':') try: s.connect((httpProxy, int(httpProxyPort))) except socket.error as e: error = [ 'socket.error', 'HTTP Proxy not running? ' '(%s) %s' % (CFG.HTTP_PROXY, e) ] # rather big problem: http proxy not running. log_error("*** ERROR ***: %s" % error[1]) Traceback(mail=0) except socket_error as e: # pylint: disable=duplicate-except error = [ 'socket.sslerror', '(%s) %s' % (CFG.HTTP_PROXY, e) ] # rather big problem: http proxy not running. log_error("*** ERROR ***: %s" % error[1]) Traceback(mail=0) else: error = ['socket', str(e)] log_error(error) Traceback(mail=0) else: log_error("Socket error", e) Traceback(mail=0) Traceback(mail=1) token = None time.sleep(.25) continue except SSL.SSL.SSLError as e: token = None error = ['rhn.SSL.SSL.SSLError', repr(e), str(e)] log_error(error) Traceback(mail=0) time.sleep(.25) continue except xmlrpclib.ProtocolError as e: token = None log_error('xmlrpclib.ProtocolError', e) time.sleep(.25) continue except xmlrpclib.Fault as e: # Report it through the mail # Traceback will try to walk over all the values # in each stack frame, and eventually will try to stringify # the method object itself # This should trick it, since the originator of the exception # is this function, instead of a deep call into xmlrpclib log_error("%s" % e) if e.faultCode == 10000: # reraise it for the users (outage or "important message" # coming through") raise_with_tb(rhnFault(e.faultCode, e.faultString), sys.exc_info()[2]) # ok... it's some other fault Traceback("ProxyAuth.login (Fault) - SUSE Manager Proxy not " "able to log in.") # And raise a Proxy Error - the server made its point loud and # clear raise_with_tb( rhnFault( 1000, _("SUSE Manager Proxy error (during proxy login). " "Please contact your system administrator.")), sys.exc_info()[2]) except Exception as e: # pylint: disable=broad-except token = None log_error("Unhandled exception", e) Traceback(mail=0) time.sleep(.25) continue else: break if not token: if error: if error[0] in ('xmlrpclib.ProtocolError', 'socket.error', 'socket'): raise rhnFault( 1000, _("SUSE Manager Proxy error (error: %s). " "Please contact your system administrator.") % error[0]) if error[0] in ('rhn.SSL.SSL.SSLError', 'socket.sslerror'): raise rhnFault( 1000, _("SUSE Manager Proxy error (SSL issues? Error: %s). " "Please contact your system administrator.") % error[0]) raise rhnFault(1002, err_text='%s' % e) raise rhnFault(1001) if self.hostname: token = token + ':' + self.hostname log_debug(6, "New proxy token: %s" % token) return token
class Repository(rhnRepository.Repository): # pylint: disable=R0902 """ Proxy local package repository lookup and manipulation code. """ def __init__(self, channelName, channelVersion, clientInfo, rhnParent=None, rhnParentXMLRPC=None, httpProxy=None, httpProxyUsername=None, httpProxyPassword=None, caChain=None): log_debug(3, channelName) rhnRepository.Repository.__init__(self, channelName) self.functions = CFG.PROXY_LOCAL_FLIST self.channelName = channelName self.channelVersion = channelVersion self.clientInfo = clientInfo self.rhnParent = rhnParent self.rhnParentXMLRPC = rhnParentXMLRPC self.httpProxy = httpProxy self.httpProxyUsername = httpProxyUsername self.httpProxyPassword = httpProxyPassword self.caChain = caChain def getPackagePath(self, pkgFilename, redirect=0): """ OVERLOADS getPackagePath in common/rhnRepository. Returns complete path to an RPM file. """ log_debug(3, pkgFilename) mappingName = "package_mapping:%s:" % self.channelName pickledMapping = self._cacheObj(mappingName, self.channelVersion, self.__channelPackageMapping, ()) mapping = cPickle.loads(pickledMapping) # If the file name has parameters, it's a different kind of package. # Determine the architecture requested so we can construct an # appropriate filename. if type(pkgFilename) == types.ListType: arch = pkgFilename[3] if isSolarisArch(arch): pkgFilename = "%s-%s-%s.%s.pkg" % \ (pkgFilename[0], pkgFilename[1], pkgFilename[2], pkgFilename[3]) if not mapping.has_key(pkgFilename): log_error("Package not in mapping: %s" % pkgFilename) raise rhnFault( 17, _("Invalid RPM package requested: %s") % pkgFilename) # A list of possible file paths. Always a list, channel mappings are # cleared on package upgrade so we don't have to worry about the old # behavior of returning a string filePaths = mapping[pkgFilename] # Can we see a file at any of the possible filepaths? for filePath in filePaths: filePath = "%s/%s" % (CFG.PKG_DIR, filePath) log_debug(4, "File path", filePath) if os.access(filePath, os.R_OK): return filePath log_debug(4, "Package not found locally: %s" % pkgFilename) raise NotLocalError(filePaths[0], pkgFilename) def getSourcePackagePath(self, pkgFilename): """ OVERLOADS getSourcePackagePath in common/rhnRepository. snag src.rpm and nosrc.rpm from local repo, after ensuring we are authorized to fetch it. """ log_debug(3, pkgFilename) if pkgFilename[-8:] != '.src.rpm' and pkgFilename[-10:] != '.nosrc.rpm': raise rhnFault( 17, _("Invalid SRPM package requested: %s") % pkgFilename) # Connect to the server to get an authorization for downloading this # package server = rpclib.Server(self.rhnParentXMLRPC, proxy=self.httpProxy, username=self.httpProxyUsername, password=self.httpProxyPassword) if self.caChain: server.add_trusted_cert(self.caChain) try: retval = server.proxy.package_source_in_channel( pkgFilename, self.channelName, self.clientInfo) except xmlrpclib.Fault, e: raise rhnFault(1000, _("Error retrieving source package: %s") % str(e)), None, sys.exc_info()[2] if not retval: raise rhnFault( 17, _("Invalid SRPM package requested: %s") % pkgFilename) if pkgFilename[-8:] != '.src.rpm': # We already know the filename ends in .src.rpm nvrea = list(parseRPMName(pkgFilename[:-8])) nvrea.append("src") else: # We already know the filename ends in .nosrc.rpm # otherwise we did not pass first if in this func nvrea = list(parseRPMName(pkgFilename[:-10])) nvrea.append("nosrc") filePaths = computePackagePaths(nvrea, source=1, prepend=PREFIX) for filePath in filePaths: filePath = "%s/%s" % (CFG.PKG_DIR, filePath) log_debug(4, "File path", filePath) if os.access(filePath, os.R_OK): return filePath log_debug(4, "Source package not found locally: %s" % pkgFilename) raise NotLocalError(filePaths[0], pkgFilename)
def fetch_token(token_string): """ Fetches a token from the database """ log_debug(3, token_string) # A token should always be passed to this function assert token_string tokens = token_string.split(',') h = rhnSQL.prepare(_query_token) result = [] rereg_token_found = 0 num_of_rereg = 0 # Global user_id and org_id user_id = None same_user_id = 1 org_id = None ks_session_id_token = None deploy_configs = None entitlements_base = {} entitlements_extra = {} # List of re-registration entitlements labels (if found): rereg_ents = [] for token in tokens: h.execute(token=token) token_entry, token_entitlements = _fetch_token_from_cursor(h) if not token_entry: # Unable to find the token log_error("Invalid token '%s'" % token) raise rhnFault(60, _("Could not find token '%s'") % token, explain=0) row = token_entry if row.get('server_id'): rereg_token_found = row num_of_rereg += 1 # Store the re-reg ents: for tup in token_entitlements.keys(): rereg_ents.append(tup[0]) # Check user_id token_user_id = row.get('user_id') # 4/27/05 wregglej - Commented this line out 'cause the token_user_id should # be allowed to be None. This line was causing problems when registering with # an activation key whose creator had been deleted. #assert(token_user_id is not None) if same_user_id and user_id is not None and user_id != token_user_id: log_debug(4, "Different user ids: %s, %s" % (same_user_id, user_id)) # This token has a different user id than the rest same_user_id = 0 else: user_id = token_user_id # Check org_id token_org_id = row.get('org_id') assert(token_org_id is not None) if org_id is not None and org_id != token_org_id: # Cannot use activation keys from different orgs raise rhnFault(63, _("Tokens from mismatching orgs"), explain=0) org_id = token_org_id # Check kickstart session ids token_ks_session_id = row.get('kickstart_session_id') if token_ks_session_id is not None: if ks_session_id_token is not None: ks_session_id = ks_session_id_token['kickstart_session_id'] if ks_session_id != token_ks_session_id: # Two tokens with different kickstart sessions raise rhnFault(63, _("Kickstart session mismatch"), explain=0) else: # This token has kickstart session id info ks_session_id_token = row # Iterate through the entitlements from this token # and intead of picking one entitlement, create a union of # all the entitlemts as a list of tuples of (name, label) aka # (token_type, token_desc) _categorize_token_entitlements(token_entitlements, entitlements_base, entitlements_extra) # Deploy configs? deploy_configs = deploy_configs or (row['deploy_configs'] == 'Y') result.append(row) # One should not stack re-activation tokens if num_of_rereg > 1: raise rhnFault(63, _("Stacking of re-registration tokens is not supported"), explain=0) entitlements_remove = [] _validate_entitlements(token_string, rereg_ents, entitlements_base, entitlements_extra, entitlements_remove) log_debug(5, "entitlements_base = %s" % entitlements_base) log_debug(5, "entitlements_extra = %s" % entitlements_extra) if ks_session_id_token: ks_session_id = ks_session_id_token['kickstart_session_id'] else: ks_session_id = None # akl add entitles array constructed above to kwargs kwargs = { 'user_id': user_id, 'org_id': org_id, 'kickstart_session_id': ks_session_id, 'entitlements': entitlements_base.keys() + entitlements_extra.keys(), 'deploy_configs': deploy_configs, } log_debug(4, "Values", kwargs) if rereg_token_found and len(result) > 1: log_debug(4, "re-activation stacked with activationkeys") kwargs['remove_entitlements'] = entitlements_remove return ReRegistrationActivationToken(result, **kwargs) elif rereg_token_found: log_debug(4, "simple re-activation") return ReRegistrationToken([rereg_token_found], **kwargs) return ActivationTokens(result, **kwargs)
def getPackageErratum(self, system_id, pkg): """ Clients v2+ - Get errata for a package given [n,v,r,e,a,...] format Sing-along: You say erratum(sing), I say errata(pl)! :) IN: pkg: [n,v,r,e,s,a,ch,...] RET: a hash by errata that applies to this package """ log_debug(5, system_id, pkg) if type(pkg) != type([]) or len(pkg) < 7: log_error("Got invalid package specification: %s" % str(pkg)) raise rhnFault(30, _("Expected a package, not: %s") % pkg) # Authenticate and decode server id. self.auth_system(system_id) # log the entry log_debug(1, self.server_id, pkg) # Stuff the action in the headers: transport = rhnFlags.get('outputTransportOptions') transport['X-RHN-Action'] = 'getPackageErratum' name, ver, rel, epoch, arch, size, channel = pkg[:7] if epoch in ['', 'none', 'None']: epoch = None # XXX: also, should arch/size/channel ever be used? # bug#186996:adding synopsis field to errata info # client side changes are needed to access this data. h = rhnSQL.prepare(""" select distinct e.id errata_id, e.advisory_type errata_type, e.advisory advisory, e.topic topic, e.description description, e.synopsis synopsis from rhnServerChannel sc, rhnChannelPackage cp, rhnChannelErrata ce, rhnErrata e, rhnErrataPackage ep, rhnPackage p where p.name_id = LOOKUP_PACKAGE_NAME(:name) and p.evr_id = LOOKUP_EVR(:epoch, :ver, :rel) -- map to a channel and p.id = cp.package_id -- map to an errata as well and p.id = ep.package_id and ep.errata_id = e.id -- the errata and the channel have to be linked and e.id = ce.errata_id and ce.channel_id = cp.channel_id -- and the server has to be subscribed to the channel and cp.channel_id = sc.channel_id and sc.server_id = :server_id """) # " emacs sucks h.execute(name=name, ver=ver, rel=rel, epoch=epoch, server_id=str(self.server_id)) return self._sanitize_result(h)
# default template values for error messages templateValues = { 'hostname': 'rhn.redhat.com', } # This array translates exception codes into meaningful messages # for the eye of the beholder # DOMAINS: # 0-999: Red Hat client/client-like interaction errors # 1000-1999: Proxy specific interaction errors # 2000-2999: Red Hat Satellite specific interation errors FaultArray = { # 0-999: Red Hat client/client-like interaction errors: 1: _("This does not appear to be a valid username."), 2: _("Invalid username and password combination."), 3: _("This login is already taken, or the password is incorrect."), 4: _("Permission denied."), 6: _("Object not found."), 8: _("Invalid System Digital ID."), 9: _("Invalid System Credentials."), 10: _("Could not retrieve user data from database."), 11:
def get_package_path(server_id, pkg_spec, channel): log_debug(3, server_id, pkg_spec, channel) if isinstance(pkg_spec, ListType): pkg = pkg_spec[:4] # Insert EPOCH pkg.insert(1, None) else: pkg = parseRPMFilename(pkg_spec) if pkg is None: log_debug(4, "Error", "Requested weird package", pkg_spec) raise rhnFault(17, _("Invalid RPM package %s requested") % pkg_spec) statement = """ select p.id, p.path path, pe.epoch epoch from rhnPackageArch pa, rhnChannelPackage cp, rhnPackage p, rhnPackageEVR pe, rhnServerChannel sc, rhnPackageName pn, rhnChannel c where 1=1 and c.label = :channel and pn.name = :name and sc.server_id = :server_id and pe.version = :ver and pe.release = :rel and c.id = sc.channel_id and c.id = cp.channel_id and pa.label = :arch and pn.id = p.name_id and p.id = cp.package_id and p.evr_id = pe.id and sc.channel_id = cp.channel_id and p.package_arch_id = pa.id """ h = rhnSQL.prepare(statement) pkg = list(map(str, pkg)) h.execute(name=pkg[0], ver=pkg[2], rel=pkg[3], arch=pkg[4], channel=channel, server_id=server_id) rs = h.fetchall_dict() if not rs: log_debug(4, "Error", "Non-existant package requested", server_id, pkg_spec, channel) raise rhnFault(17, _("Invalid RPM package %s requested") % pkg_spec) # It is unlikely for this query to return more than one row, # but it is possible # (having two packages with the same n, v, r, a and different epoch in # the same channel is prohibited by the RPM naming scheme; but extra # care won't hurt) max_row = rs[0] for each in rs[1:]: # Compare the epoch as string if _none2emptyString(each['epoch']) > _none2emptyString( max_row['epoch']): max_row = each # Set the flag for the proxy download accelerator rhnFlags.set("Download-Accelerator-Path", max_row['path']) return check_package_file(max_row['path'], max_row['id'], pkg_spec), max_row['id']
def __checkAuthSessionTokenCache(self, token, channel): """ Authentication / authorize the channel """ log_debug(2, token, channel) self.clientServerId = token['X-RHN-Server-ID'] cachedToken = self.proxyAuth.get_client_token(self.clientServerId) if not cachedToken: # maybe client logged in through different load-balanced proxy # try to update the cache an try again cachedToken = self.proxyAuth.update_client_token_if_valid( self.clientServerId, token) if not cachedToken: msg = _("Invalid session key - server ID not found in cache: %s") \ % self.clientServerId log_error(msg) raise rhnFault(33, msg) self.cachedClientInfo = UserDictCase(cachedToken) clockSkew = self.cachedClientInfo["X-RHN-Auth-Proxy-Clock-Skew"] del self.cachedClientInfo["X-RHN-Auth-Proxy-Clock-Skew"] # Add the server id self.authChannels = self.cachedClientInfo['X-RHN-Auth-Channels'] del self.cachedClientInfo['X-RHN-Auth-Channels'] self.cachedClientInfo['X-RHN-Server-ID'] = self.clientServerId log_debug(4, 'Retrieved token from cache: %s' % self.cachedClientInfo) # Compare the two things if not _dictEquals(token, self.cachedClientInfo, ['X-RHN-Auth-Channels']): # Maybe the client logged in through a different load-balanced # proxy? Check validity of the token the client passed us. updatedToken = self.proxyAuth.update_client_token_if_valid( self.clientServerId, token) # fix up the updated token the same way we did above if updatedToken: self.cachedClientInfo = UserDictCase(updatedToken) clockSkew = self.cachedClientInfo[ "X-RHN-Auth-Proxy-Clock-Skew"] del self.cachedClientInfo["X-RHN-Auth-Proxy-Clock-Skew"] self.authChannels = self.cachedClientInfo[ 'X-RHN-Auth-Channels'] del self.cachedClientInfo['X-RHN-Auth-Channels'] self.cachedClientInfo['X-RHN-Server-ID'] = \ self.clientServerId log_debug( 4, 'Retrieved token from cache: %s' % self.cachedClientInfo) if not updatedToken or not _dictEquals( token, self.cachedClientInfo, ['X-RHN-Auth-Channels']): log_debug(3, "Session tokens different") raise rhnFault(33) # Invalid session key # Check the expiration serverTime = float(token['X-RHN-Auth-Server-Time']) offset = float(token['X-RHN-Auth-Expire-Offset']) if time.time() > serverTime + offset + clockSkew: log_debug(3, "Session token has expired") raise rhnFault(34) # Session key has expired # Only autherized channels are the ones stored in the cache. authChannels = [x[0] for x in self.authChannels] log_debug(4, "Auth channels: '%s'" % authChannels) # Check the authorization if channel not in authChannels: log_debug(4, "Not subscribed to channel %s; unauthorized" % channel) raise rhnFault(35, _('Unauthorized channel access requested.'))
def __callLocalRepository(self, req_type, identifier, funct, params): """ Contacts the local repository and retrieves files""" log_debug(2, req_type, identifier, funct, params) # NOTE: X-RHN-Proxy-Auth described in broker/rhnProxyAuth.py if rhnFlags.get('outputTransportOptions').has_key('X-RHN-Proxy-Auth'): self.cachedClientInfo['X-RHN-Proxy-Auth'] = rhnFlags.get( 'outputTransportOptions')['X-RHN-Proxy-Auth'] if rhnFlags.get('outputTransportOptions').has_key('Host'): self.cachedClientInfo['Host'] = rhnFlags.get( 'outputTransportOptions')['Host'] if req_type == 'tinyurl': try: rep = rhnRepository.TinyUrlRepository( identifier, self.cachedClientInfo, rhnParent=self.rhnParent, rhnParentXMLRPC=self.rhnParentXMLRPC, httpProxy=self.httpProxy, httpProxyUsername=self.httpProxyUsername, httpProxyPassword=self.httpProxyPassword, caChain=self.caChain, systemId=self.proxyAuth.get_system_id()) except rhnRepository.NotLocalError: return None elif req_type == 'ks-dist': try: rep = rhnRepository.KickstartRepository( identifier, self.cachedClientInfo, rhnParent=self.rhnParent, rhnParentXMLRPC=self.rhnParentXMLRPC, httpProxy=self.httpProxy, httpProxyUsername=self.httpProxyUsername, httpProxyPassword=self.httpProxyPassword, caChain=self.caChain, orgId=params['orgId'], child=params['child'], session=params['session'], systemId=self.proxyAuth.get_system_id()) except rhnRepository.NotLocalError: return None params = [params['file']] else: # Find the channel version version = None for c in self.authChannels: ch, ver = c[:2] if ch == identifier: version = ver break # We already know he's subscribed to this channel # channel, so the version is non-null rep = rhnRepository.Repository( identifier, version, self.cachedClientInfo, rhnParent=self.rhnParent, rhnParentXMLRPC=self.rhnParentXMLRPC, httpProxy=self.httpProxy, httpProxyUsername=self.httpProxyUsername, httpProxyPassword=self.httpProxyPassword, caChain=self.caChain) f = rep.get_function(funct) if not f: raise rhnFault( 1000, _("Spacewalk Proxy configuration error: invalid function %s") % funct) log_debug(3, "Calling %s(%s)" % (funct, params)) if params is None: params = () try: ret = f(*params) except rhnRepository.NotLocalError: # The package is not local return None return ret
def handler(self): """ Main handler to handle all requests pumped through this server. """ # pylint: disable=R0915 log_debug(1) self._prepHandler() _oto = rhnFlags.get('outputTransportOptions') # tell parent that we can follow redirects, even if client is not able to _oto['X-RHN-Transport-Capability'] = "follow-redirects=3" # No reason to put Host: in the header, the connection object will # do that for us # Add/modify the X-RHN-IP-Path header. ip_path = None if 'X-RHN-IP-Path' in _oto: ip_path = _oto['X-RHN-IP-Path'] log_debug(4, "X-RHN-IP-Path is: %s" % repr(ip_path)) client_ip = self.req.connection.remote_ip if ip_path is None: ip_path = client_ip else: ip_path += ',' + client_ip _oto['X-RHN-IP-Path'] = ip_path # NOTE: X-RHN-Proxy-Auth described in broker/rhnProxyAuth.py if 'X-RHN-Proxy-Auth' in _oto: log_debug( 5, 'X-RHN-Proxy-Auth currently set to: %s' % repr(_oto['X-RHN-Proxy-Auth'])) else: log_debug(5, 'X-RHN-Proxy-Auth is not set') if self.req.headers_in.has_key('X-RHN-Proxy-Auth'): tokens = [] if 'X-RHN-Proxy-Auth' in _oto: tokens = _oto['X-RHN-Proxy-Auth'].split(',') log_debug(5, 'Tokens: %s' % tokens) # GETs: authenticate user, and service local GETs. getResult = self.__local_GET_handler(self.req) if getResult is not None: # it's a GET request return getResult # 1. check cached version of the proxy login, # snag token if there... # if not... login... # if good token, cache it. # 2. push into headers. authToken = self.proxyAuth.check_cached_token() log_debug(5, 'Auth token for this machine only! %s' % authToken) tokens = [] _oto = rhnFlags.get('outputTransportOptions') if _oto.has_key('X-RHN-Proxy-Auth'): log_debug( 5, ' (auth token prior): %s' % repr(_oto['X-RHN-Proxy-Auth'])) tokens = _oto['X-RHN-Proxy-Auth'].split(',') # list of tokens to be pushed into the headers. tokens.append(authToken) tokens = [t for t in tokens if t] _oto['X-RHN-Proxy-Auth'] = ','.join(tokens) log_debug( 5, ' (auth token after): %s' % repr(_oto['X-RHN-Proxy-Auth'])) log_debug(3, 'Trying to connect to parent') # Loops twice? Here's why: # o If no errors, the loop is broken and we move on. # o If an error, either we get a new token and try again, # or we get a critical error and we fault. for _i in range(2): self._connectToParent() # part 1 log_debug(4, 'after _connectToParent') # Add the proxy version rhnFlags.get('outputTransportOptions' )['X-RHN-Proxy-Version'] = str(_PROXY_VERSION) status = self._serverCommo() # part 2 # check for proxy authentication blowup. respHeaders = self.responseContext.getHeaders() if not respHeaders or \ not respHeaders.has_key('X-RHN-Proxy-Auth-Error'): # No proxy auth errors # XXX: need to verify that with respHeaders == # None that is is correct logic. It should be -taw break error = str(respHeaders['X-RHN-Proxy-Auth-Error']).split(':')[0] # If a proxy other than this one needs to update its auth token # pass the error on up to it if (respHeaders.has_key('X-RHN-Proxy-Auth-Origin') and respHeaders['X-RHN-Proxy-Auth-Origin'] != self.proxyAuth.hostname): break # Expired/invalid auth token; go through the loop once again if error == '1003': # invalid token msg = "Spacewalk Proxy Session Token INVALID -- bad!" log_error(msg) log_debug(0, msg) elif error == '1004': log_debug( 1, "Spacewalk Proxy Session Token expired, acquiring new one." ) else: # this should never happen. msg = "Spacewalk Proxy login failed, error code is %s" % error log_error(msg) log_debug(0, msg) raise rhnFault( 1000, _("Spacewalk Proxy error (issues with proxy login). " "Please contact your system administrator.")) # Forced refresh of the proxy token rhnFlags.get('outputTransportOptions')[ 'X-RHN-Proxy-Auth'] = self.proxyAuth.check_cached_token(1) else: # for # The token could not be aquired log_debug(0, "Unable to acquire proxy authentication token") raise rhnFault( 1000, _("Spacewalk Proxy error (unable to acquire proxy auth token). " "Please contact your system administrator.")) # Support for yum byte-range if (status != apache.OK) and (status != apache.HTTP_PARTIAL_CONTENT): log_debug(1, "Leaving handler with status code %s" % status) return status self.__handleAction(self.responseContext.getHeaders()) return self._clientCommo()
def __repr__(self): return _("Invalid request received (%s).") % self.__value
def token_channels(server, server_arch, tokens_obj): """ Handle channel subscriptions for the registration token """ assert(isinstance(tokens_obj, ActivationTokens)) server_id, server_arch_id = server['id'], server['server_arch_id'] # what channels are associated with this token (filter only those # compatible with this server) h = rhnSQL.prepare(""" select rtc.channel_id id, c.name, c.label, c.parent_channel from rhnRegTokenChannels rtc, rhnChannel c, rhnServerChannelArchCompat scac where rtc.token_id = :token_id and rtc.channel_id = c.id and c.channel_arch_id = scac.channel_arch_id and scac.server_arch_id = :server_arch_id """) chash = {} base_channel_token = None base_channel_id = None for token in tokens_obj.tokens: token_id = token['token_id'] h.execute(token_id=token_id, server_arch_id=server_arch_id) while 1: row = h.fetchone_dict() if not row: break channel_id = row['id'] chash[channel_id] = row if row['parent_channel'] is not None: # Not a base channel continue # We only allow for one base channel if base_channel_id is not None and channel_id != base_channel_id: # Base channels conflict - are they coming from the same # token? if base_channel_token == token: log_error("Token has multiple base channels", token_id, base_channel_id) raise rhnFault(62, _("Token `%s' has more than one base channel assigned") % token['note']) raise rhnFault(63, _("Conflicting base channels")) base_channel_id = channel_id base_channel_token = token bc = chash.get(base_channel_id) log_debug(4, "base channel", bc) # get the base channel for this server # Note that we are hitting this codepath after newserver.__save() has been # run, which means we've already chosen a base channel # from rhnDistChannelMap sbc = rhnChannel.get_base_channel(server_id, none_ok=1) # prepare the return value ret = [] # now try to figure out which base channel we prefer if bc is None: if sbc is None: # we need at least one base channel definition log_error("Server has invalid release and " "token contains no base channels", server_id, tokens_obj.tokens) ret.append("System registered without a base channel") ret.append("Unsupported release-architecture combination " "(%s, %s)" % (server["release"], server_arch)) return ret else: # do we need to drop the one from sbc? if sbc and sbc["id"] != bc["id"]: # we need to prefer the token one # unsubscribe from old channel(s) rhnChannel.unsubscribe_all_channels(server_id) sbc = None # force true on the next test if sbc is None: # no base channel subscription at this point try: rhnChannel._subscribe_sql(server_id, bc["id"], commit=0) except rhnChannel.SubscriptionCountExceeded: ret.append("System registered without a base channel: " "subscription count exceeded for channel %s (%s)" % (bc["name"], bc["label"])) return ret ret.append("Subscribed to base channel '%s' (%s)" % ( bc["name"], bc["label"])) sbc = bc # attempt to subscribe all non-base channels associated with this # token subscribe_channel = rhnSQL.Procedure("rhn_channel.subscribe_server") # Use a set here to ensure uniqueness of the # channel family ids used in the loop below. channel_family_ids = set() for c in filter(lambda a: a["parent_channel"], chash.values()): # make sure this channel has the right parent if str(c["parent_channel"]) != str(sbc["id"]): ret.append("NOT subscribed to channel '%s' " "(not a child of '%s')" % ( c["name"], sbc["name"])) continue try: # don't run the EC yet # XXX: test return code when this one will start returning # a status subscribe_channel(server_id, c["id"], 0, None, 0) child = rhnChannel.Channel() child.load_by_id(c["id"]) child._load_channel_families() cfamid = child._channel_families[0] channel_family_ids.add(cfamid) except rhnSQL.SQLError, e: log_error("Failed channel subscription", server_id, c["id"], c["label"], c["name"]) ret.append("FAILED to subscribe to channel '%s'" % c["name"]) else: ret.append("Subscribed to channel '%s'" % c["name"])
class Shelf: """ Client authenication temp. db. Main class that the client side (client to the caching daemon) has to instantiate to expose the proper API. Basically, the API is a dictionary. """ # pylint: disable=R0903 def __init__(self, server_addr): log_debug(6, server_addr) self.serverAddr = server_addr def __request(self, methodname, params): # pylint: disable=R0915 log_debug(6, methodname, params) # Init the socket sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: sock.connect(self.serverAddr) except socket.error, e: sock.close() methodname = None log_error("Error connecting to the auth cache: %s" % str(e)) Traceback("Shelf.__request", extra=""" Error connecting to the the authentication cache daemon. Make sure it is started on %s""" % str(self.serverAddr)) # FIXME: PROBLEM: this rhnFault will never reach the client raise rhnFault( 1000, _("Spacewalk Proxy error (issues connecting to auth cache). " "Please contact your system administrator") ), None, sys.exc_info()[2] wfile = sock.makefile("w") try: send(wfile, methodname, None, *params) except CommunicationError: wfile.close() sock.close() Traceback("Shelf.__request", extra="Encountered a CommunicationError") raise except socket.error: wfile.close() sock.close() log_error("Error communicating to the auth cache: %s" % str(e)) Traceback("Shelf.__request", extra="""\ Error sending to the authentication cache daemon. Make sure the authentication cache daemon is started""") # FIXME: PROBLEM: this rhnFault will never reach the client raise rhnFault( 1000, _("Spacewalk Proxy error (issues connecting to auth cache). " "Please contact your system administrator") ), None, sys.exc_info()[2] wfile.close() rfile = sock.makefile("r") try: params, methodname = recv(rfile) except CommunicationError, e: log_error(e.faultString) rfile.close() sock.close() log_error("Error communicating to the auth cache: %s" % str(e)) Traceback("Shelf.__request", extra="""\ Error receiving from the authentication cache daemon. Make sure the authentication cache daemon is started""") # FIXME: PROBLEM: this rhnFault will never reach the client raise rhnFault( 1000, _("Spacewalk Proxy error (issues communicating to auth cache). " "Please contact your system administrator") ), None, sys.exc_info()[2]
def submit(self, system_id, action_id, result, message="", data={}): """ Submit the results of a queue run. Maps old and new rhn_check behavior to new database status codes The new API uses 4 slightly different status codes than the old client does. This function will "hopefully" sensibly map them. Old methodology: -rhn_check retrieves an action from the top of the action queue. -It attempts to execute the desired action and returns either (a) 0 -- presumed successful. (b) rhnFault object -- presumed failed (c) some other non-fault object -- *assumed* successful. -Regardless of result code, action is marked as "executed" We try to make a smarter status selection (i.e. failed||completed). For reference: New DB status codes: Old DB status codes: 0: Queued 0: queued 1: Picked Up 1: picked up 2: Completed 2: executed 3: Failed 3: completed """ if type(action_id) is not IntType: # Convert it to int try: action_id = int(action_id) except ValueError: log_error("Invalid action_id", action_id) raise_with_tb(rhnFault(30, _("Invalid action value type %s (%s)") % (action_id, type(action_id))), sys.exc_info()[2]) # Authenticate the system certificate self.auth_system(system_id) log_debug(1, self.server_id, action_id, result) # check that the action is valid # We have a uniqueness constraint on (action_id, server_id) h = rhnSQL.prepare(""" select at.label action_type, at.trigger_snapshot, at.name from rhnServerAction sa, rhnAction a, rhnActionType at where sa.server_id = :server_id and sa.action_id = :action_id and sa.status = 1 and a.id = :action_id and a.action_type = at.id """) h.execute(server_id=self.server_id, action_id=action_id) row = h.fetchone_dict() if not row: log_error("Server %s does not own action %s" % ( self.server_id, action_id)) raise rhnFault(22, _("Action %s does not belong to server %s") % ( action_id, self.server_id)) action_type = row['action_type'] trigger_snapshot = (row['trigger_snapshot'] == 'Y') if 'missing_packages' in data: missing_packages = "Missing-Packages: %s" % str( data['missing_packages']) rmsg = "%s %s" % (message, missing_packages) elif 'koan' in data: rmsg = "%s: %s" % (message, data['koan']) else: rmsg = message rcode = result # Careful with this one, result can be a very complex thing # and this processing is required for compatibility with old # rhn_check clients if type(rcode) == type({}): if "faultCode" in result: rcode = result["faultCode"] if "faultString" in result: rmsg = result["faultString"] + str(data) if type(rcode) in [type({}), type(()), type([])] \ or type(rcode) is not IntType: rmsg = u"%s [%s]" % (UnicodeType(message), UnicodeType(rcode)) rcode = -1 # map to db codes. status = self.status_for_action_type_code(action_type, rcode) if status == 3: # Failed action - invalidate children self._invalidate_child_actions(action_id) elif action_type == 'reboot.reboot': # reboot action should stay as pickup rhnSQL.commit() return 0 elif status == 2 and trigger_snapshot and self.__should_snapshot(): # if action status is 'Completed', snapshot if allowed and if needed self.server.take_snapshot("Scheduled action completion: %s" % row['name']) self.__update_action(action_id, status, rcode, rmsg) # Store the status in a flag - easier than to complicate the action # plugin API by adding a status rhnFlags.set('action_id', action_id) rhnFlags.set('action_status', status) self.process_extra_data(self.server_id, action_id, data=data, action_type=action_type) # commit, because nobody else will rhnSQL.commit() return 0
# in each stack frame, and eventually will try to stringify # the method object itself # This should trick it, since the originator of the exception # is this function, instead of a deep call into xmlrpclib log_error("%s" % e) if e.faultCode == 10000: # reraise it for the users (outage or "important message" # coming through") raise rhnFault(e.faultCode, e.faultString), None, sys.exc_info()[2] # ok... it's some other fault Traceback("ProxyAuth.login (Fault) - Spacewalk Proxy not " "able to log in.") # And raise a Proxy Error - the server made its point loud and # clear raise rhnFault(1000, _("Spacewalk Proxy error (during proxy login). " "Please contact your system administrator.")), None, sys.exc_info()[2] except Exception, e: # pylint: disable=E0012, W0703 token = None log_error("Unhandled exception", e) Traceback(mail=0) time.sleep(.25) continue else: break if not token: if error: if error[0] in ('xmlrpclib.ProtocolError', 'socket.error', 'socket'): raise rhnFault(1000, _("Spacewalk Proxy error (error: %s). " "Please contact your system administrator.") % error[0])