def _check_rpcvers(self, msg): """Returns True if rpcvers is ok, otherwise sends out MSG_DENIED""" if msg.rpcvers not in self.rpcversions: log_t.warn("RPC_MISMATCH, do not support vers=%i" % msg.rpcvers) raise rpclib.RPCDeniedReply(RPC_MISMATCH, (min(self.rpcversions), max(self.rpcversions)))
def _check_auth(self, msg, data): """Returns security module to use if call processing should continue, otherwise returns None. Note that it is possible for security module to hijack call processing. """ # Check that flavor is supported try: sec = self.sec_flavors[msg.cred.flavor] except KeyError: log_t.warn("AUTH_ERROR: Unsupported flavor %i" % msg.cred.flavor) if msg.proc == 0 and msg.cred.flavor == AUTH_NONE: # RFC 1831 section 11.1 says "by convention" should allow this log_t.warn("Allowing NULL proc through anyway") sec = security.klass(AUTH_NONE)() else: raise rpclib.RPCDeniedReply(AUTH_ERROR, AUTH_FAILED) # Call flavor specific authority checks return sec.check_auth(msg, data) # What incoming flavors do I allow? # How does server learn/change these defaults # For AUTH_NONE: # return True - note 11.1 says "by convention" should # allow AUTH_NONE, at least for proc==0 # For AUTH_SYS: # check machinename, mode - again how is accept list set on server? # For GSS: # illegal enum values should return AUTH_BADCRED # this will be noticed by XDR unpack failing, which means # type(cred.body) == str # check gss_version, fail with AUTH_BADCRED # check allows service - again how does server set? # check context handle - what does this mean? # see 5.3.3.3, we maintain list of contexts we are in session # with, if not in list, return CREDPROBLEM # if security credentials expire, return CTXPROBLEM # check header checksum in verf, failure returns CREDPROBLEM # check seq_num in cred, silently drop repeats, # return CTXPROBLEM if exceeds window # check seq_num in data, return GARBAGE_ARGS if mismatches cred # check gss_proc==DATA, else: # if proc==0, handle elsewhere # else return AUTH_BADCRED return True
def _event_rpc_call(self, msg, msg_data, pipe): """Deal with an incoming RPC CALL. msg is unpacked header, with length fields added. msg_data is raw procedure data. """ """Given an RPC record, returns appropriate reply This is run in its own thread. """ class XXX(object): pass call_info = XXX() # Store various info we need to pass to procedure call_info.header_size = msg.length call_info.payload_size = len(msg_data) call_info.connection = pipe notify = None try: # Check for reasons to DENY the call try: self._check_rpcvers(msg) call_info.credinfo = self._check_auth(msg, msg_data) except rpclib.RPCFlowContol: raise except Exception: log_t.warn("Problem with incoming call, returning AUTH_FAILED", exc_info=True) raise rpclib.RPCDeniedReply(AUTH_ERROR, AUTH_FAILED) # Call has been ACCEPTED, now check for reasons not to succeed sec = call_info.credinfo.sec msg_data = sec.unsecure_data(msg.body.cred, msg_data) if not self._check_program(msg.prog): log_t.warn("PROG_UNAVAIL, do not support prog=%i" % msg.prog) raise rpclib.RPCUnsuccessfulReply(PROG_UNAVAIL) low, hi = self._version_range(msg.prog) if not self._check_version(low, hi, msg.vers): log_t.warn("PROG_MISMATCH, do not support vers=%i" % msg.vers) raise rpclib.RPCUnsuccessfulReply(PROG_MISMATCH, (low, hi)) method = self._find_method(msg) if method is None: log_t.warn("PROC_UNAVAIL for vers=%i, proc=%i" % (msg.vers, msg.proc)) raise rpclib.RPCUnsuccessfulReply(PROC_UNAVAIL) # Everything looks good at this layer, time to do the call tuple = method(msg_data, call_info) if len(tuple) == 2: status, result = tuple else: status, result, notify = tuple if result is None: result = '' if not isinstance(result, basestring): raise TypeError("Expected string") # status, result = method(msg_data, call_info) log_t.debug("Called method, got %r, %r" % (status, result)) except rpclib.RPCDrop: # Silently drop the request self._notify_drop() return except rpclib.RPCFlowContol, e: body, data = e.body()
def auth_error(code): """Return (MSG_DENIED, AUTH_ERROR, code)""" raise rpclib.RPCDeniedReply(AUTH_ERROR, code)