def roundDone(self, responses, key, x): self.debugpath.append("FV: roundDone %d" % x) if self.done: result = {} # see if everyone's responses agreed... for success, resp in responses: # only look at successful non-kData (dict) responses. if success and resp != None and not isinstance(resp, dict): if result.has_key(resp): result[resp] += 1 else: result[resp] = 1 if len(result) == 0: # ... if no one responded, XXX: do something orther than None? logger.info("couldn't get any results") return None elif len(result) == 1: # ... if they did, return the result return result.keys()[0] else: # ... otherwise, return the result of the majority # (other options include returning all results) logger.info("got conflicting results, determining best...") quorumResult = None bestScore = 0 for r in result: #logger.debug("result %s scored %d" % (r, result[r])) if result[r] > bestScore: bestScore = result[r] quorumResult = r #logger.debug("result %s is new best" % r) logger.info("returning result %s", fdecode(quorumResult)) return quorumResult
def listMeta(config): fmaster = open(os.path.join(config.metadir,config.metamaster), 'r') master = fmaster.read() fmaster.close() if master == "": master = {} else: master = fdecode(master) return master
def getChallenge(challenge): try: challenge = fdecode(challenge) except: pass if outstandingChallenges.has_key(challenge): return outstandingChallenges[challenge] else: return None
def loadMasterMeta(self): """ loads fname->sK mappings from file """ fmaster = open(os.path.join(self.metadir, self.metamaster), 'r') master = fmaster.read() fmaster.close() if master == "": master = {} else: master = fdecode(master) self.master = master
def answerChallenge(challenge, Kr, groupIDu, sID, headers={}): loggerauth.debug("got challenge: '%s'" % challenge) sID = binascii.unhexlify(sID) challenge = (fdecode(challenge),) response = fencode(Kr.decrypt(challenge)) # XXX: RSA.decrypt won't restore leading 0's. This causes # some challenges to fail when they shouldn't -- solved for now # on the server side by generating non-0 leading challenges. loggerauth.debug("decrypted challenge to %s" % response) responseID = fdecode(response)[:len(sID)] loggerauth.debug(" response id: %s" % fencode(responseID)) if responseID != sID: # fail the op. # If we don't do this, we may be allowing the server to build a # dictionary useful for attack. The attack is as follows: node A # (server) collects a bunch of un-IDed challenge/response pairs by # issuing challenges to node B (client). Then node A uses those # responses to pose as B to some other server C. This sounds # farfetched, in that such a database would need to be huge, but in # reality, such an attack can happen in real-time, with node A # simultaneously serving requests from B, relaying challenges from C to # B, and then responding with B's responses to C to gain resources # there as an imposter. The ID string prevents this attack. # XXX: trust-- (must go by ip:port, since ID could be innocent) raise ImposterException("node %s is issuing invalid challenges --" " claims to have id=%s" % (fencode(sID), fencode(responseID))) response = fdecode(response)[len(sID):] loggerauth.debug(" challenge response: '%s'" % fencode(response)) response = fencode(response)+":"+groupIDu loggerauth.debug("response:groupIDu=%s" % response) response = binascii.b2a_base64(response) loggerauth.debug("b64(response:groupIDu)=%s" % response) response = "Basic %s" % response headers['Authorization'] = response return headers
def endtests(res, nKu, node, host, port): """ executes after all tests """ try: res = fdecode(res) except ValueError: pass if res != testval: return testerror(None, "retrieved value does not match stored value:" " '%s' != '%s'" % (res, testval), node) logger.info("testkFindVal PASSED") logger.debug("testkFindVal result: %s" % str(res)) logger.info("all tests PASSED") return res
def expireChallenge(challenge, expired=False): try: challenge = fdecode(challenge) except: pass if outstandingChallenges.has_key(challenge): del outstandingChallenges[challenge] if expired: # XXX: should put these in an expired challenge list so that we # can send a more useful message on failure (must then do # expirations on expired list -- maybe better to just make # these expirations really liberal). loggerauth.debug("expired challenge %s" % fencode(challenge)) else: loggerauth.debug("deleted challenge %s" % fencode(challenge))
def __init__(self, fname, mode): # XXX: need to check fname and throw if it isn't a proper BlockFile self._fname = fname self.mode = mode self._file = __builtin__.open(fname, mode) sizeString = self._file.read(8) self._size = struct.unpack('=Q',sizeString)[0] self._dataend = 8 + self._size self._file.seek(self._dataend) self._accounting = fdecode(self._file.read()) self._accountingcrc = crc32(str(self._accounting)) self._changed = False if mode[0] == 'a': self._file.seek(self._dataend) else: self._file.seek(8)
def lineReceived(self, line): logger.debug("received line '%s'" % line) command = line[0:4] if not command in VALIDOPS: print "error: invalid command op ('%s')-- "\ " are you trying to connect to the wrong port"\ " (local client port is usually external port + 500)?"\ % command return None status = line[4] data = line[5:] if not self.auth: if command == "AUTH" and status == '?': # got challenge, send response logger.debug("got AUTH challenge, sending response") echallenge = data self.sendLine("AUTH:"+self.factory.answerChallenge(echallenge)) return elif command == "AUTH" and status == ':': # response accepted, authenticated logger.debug("AUTH challenge accepted, success") self.auth = True self.factory.clientReady(self) #print "authenticated" else: if command == "AUTH" and status == "!": logger.warn("authentication failed (is FLUDHOME set" " correctly?)") print "authentication failed (is FLUDHOME set correctly?)" else: logger.warn("unknown message received before being" " authenticated:") logger.warn(" %s : %s" % (command, status)) print "unknown message received before being authenticated:" print " %s : %s" % (command, status) self.factory.setDie() elif command == "DIAG": subcommand = data[:4] data = data[4:] if subcommand == "NODE": logger.debug("DIAG NODE: %s" % data) data = fdecode(data) result = "" for i in data: score = '%d' % i[4] petID = "%064x" % i[2] netID = "%s:%d" % (i[0], i[1]) if i[5] != 0: now = int(time.time()) throttle = '(%d)' % (i[5]-now) petID = petID[:(70-len(netID)-len(throttle))]+"... " \ +throttle else: petID = petID[:(70-len(netID))]+"..." result += "%s %s %s\n" % (score, netID, petID) result += "%d known nodes\n" % len(data) d = self.factory.pending['NODE'].pop('') d.callback(result) return if subcommand == "BKTS": logger.debug("DIAG BKTS") data = fdecode(data) result = "" for i in data: for bucket in i: result += "Bucket %s:\n" % bucket for k in i[bucket]: id = "%064x" % k[2] netID = "%s:%d" % (k[0], k[1]) result += " %s %s...\n" \ % (netID,id[:72-len(netID)]) d = self.factory.pending['BKTS'].pop('') d.callback(result) return elif status == ':': response, data = data.split(status, 1) logger.debug("DIAG %s: success" % subcommand) d = self.factory.pending[subcommand].pop(data) d.callback(fdecode(response)) elif status == "!": response, data = data.split(status, 1) logger.debug("DIAG %s: failure" % subcommand) d = self.factory.pending[subcommand].pop(data) d.errback(failure.DefaultException(response)) elif status == ':': response, data = data.split(status, 1) logger.debug("%s: success" % command) d = self.factory.pending[command].pop(data) d.callback(fdecode(response)) elif status == "!": response, data = data.split(status, 1) logger.debug("%s: failure" % command) if self.factory.pending.has_key(command): if data not in self.factory.pending[command]: #print "data key is '%s'" % data print "pending is '%s'" % self.factory.pending[command] if len(self.factory.pending[command]): d = self.factory.pending[command].popitem() d.errback(failure.DefaultException(response)) else: d = self.factory.pending[command].pop(data) d.errback(failure.DefaultException(response)) else: print "failed command '%s' not in pending?" % command print "pending is: %s" % self.factory.pending if command != 'AUTH' and command != 'DIAG' and \ not None in self.factory.pending[command].values(): logger.debug("%s done at %s" % (command, time.ctime()))
def answerChallenge(self, echallenge): logger.debug("answering challenge") echallenge = (fdecode(echallenge),) challenge = self.config.Kr.decrypt(echallenge) return challenge
def doOp(self, command, fname): #print "got command '%s'" % command if command == "PUTF": logger.debug("PUTF %s", fname); return FileOps.StoreFile(self.factory.node, fname).deferred elif command == "GETI": logger.debug("GETI %s", fname); return FileOps.RetrieveFile(self.factory.node, fname).deferred elif command == "GETF": logger.debug("GETF %s", fname); return FileOps.RetrieveFilename(self.factory.node, fname).deferred elif command == "FNDN": logger.debug("FNDN %s" % fname); try: intval = long(fname, 16) except: return defer.fail("fname was not hex") return self.factory.node.client.kFindNode(intval) # The following is for testing aggregation of kFindNode on same key #dl = [] #for i in [1,2,3,4,5]: # d = self.factory.node.client.kFindNode(intval) # dl.append(d) #dlist = defer.DeferredList(dl) #return dlist elif command == "FNDV": logger.debug("FNDV %s", fname); try: intval = long(fname, 16) except: return defer.fail("fname was not hex") return self.factory.node.client.kFindValue(intval) elif command == "CRED": passphrase, email = fdecode(fname) # XXX: allow an optional passphrase hint to be sent in email. passphrase = self.factory.node.config.Kr.decrypt(passphrase) logger.debug("CRED %s to %s", passphrase, email); Kr = self.factory.node.config.Kr.exportPrivateKey() Kr['g'] = self.factory.node.config.groupIDr fKr = fencode(Kr) key = AES.new(binascii.unhexlify(hashstring(passphrase))) fKr = '\x00'*(16-(len(fKr)%16))+fKr efKr = fencode(key.encrypt(fKr)) logger.debug("efKr = %s " % efKr) d = smtp.sendmail('localhost', "your_flud_client@localhost", email, "Subject: Your encrypted flud credentials\n\n" "Hopefully, you'll never need to use this email. Its " "sole purpose is to help you recover your data after a " "catastrophic and complete loss of the original computer " "or hard drive.\n\n" "In that unlucky event, you'll need a copy of your flud " "credentials, which I've included below, sitting between " "the \"---+++---\" markers. These credentials were " "encrypted with a passphrase of your choosing when you " "installed the flud software. I'll only say this " "once:\n\n" "YOU MUST REMEMBER THAT PASSWORD IN ORDER TO RECOVER YOUR " "CREDENTIALS. If you are unable to remember the " "passphrase and your computer fails catastrophically " "(losing its local copy of these credentials), you will " "not be able to recover your data." "\n\n" "Luckily, that's all you should ever need in order to " "recover all your data: your passphrase and these " "credentials." "\n\n" "Please save this email. You may want to print out hard " "copies and store them safely, forward this email to " "other email accounts, etc. Since the credentials are " "encrypted, others won't be able to steal them " "without guessing your passphrase. " "\n\n" "---+++---\n"+efKr+"\n---+++---\n") return d # to decode this email, we search for the '---+++---' markers, make # sure the intervening data is all in one piece (remove any line # breaks \r or \n inserted by email clients) and call this 'cred', # reconstruct the AES key with the H(passphrase) (as above), and # then use the key to .decrypt(fdecode(cred)) and call this dcred, # then fdecode(dcred[dcred.find('d'):]) and call this ddcred, and # finally importPrivateKey(ddcred) and set groupIDr to ddcred['g']. elif command == "LIST": logger.debug("LIST") return defer.succeed(self.factory.config.master) elif command == "GETM": logger.debug("GETM") return FileOps.RetrieveMasterIndex(self.factory.node).deferred elif command == "PUTM": logger.debug("PUTM") return FileOps.UpdateMasterIndex(self.factory.node).deferred else: #print "fname is '%s'" % fname host = fname[:fname.find(':')] port = fname[fname.find(':')+1:fname.find(',')] fname = fname[fname.find(',')+1:] print "%s: %s : %s , %s" % (command, host, port, fname) if command == "STOR": logger.debug("STOR"); return self.factory.node.client.sendStore(fname, None, host, int(port)) elif command == "RTRV": logger.debug("RTRV"); return self.factory.node.client.sendRetrieve(fname, host, int(port)) elif command == "VRFY": logger.debug("VRFY"); offset = port[port.find(':')+1:port.find('-')] length = port[port.find('-')+1:] port = port[:port.find(':')] print "%s: %s : %s %s - %s , %s" % (command, host, port, offset, length, fname) return self.factory.node.client.sendVerify(fname, int(offset), int(length), host, int(port)) else: logger.debug("bad op"); return defer.fail("bad op")
required = ('Ku_e', 'Ku_n', 'port', 'offset', 'length') params = requireParams(request, required) except Exception, inst: msg = inst.args[0] + " in request received by VERIFY" loggervrfy.log(logging.INFO, msg) request.setResponseCode(http.BAD_REQUEST, "Bad Request") loggervrfy.debug("BAD REQUEST") return msg else: host = getCanonicalIP(request.getClientIP()) port = int(params['port']) loggervrfy.log(logging.INFO, "received VERIFY request from %s:%s...", host, port) if 'meta' in request.args: params['metakey'] = request.args['metakey'][0] params['meta'] = fdecode(request.args['meta'][0]) loggerretr.info("VERIFY contained meta field with %d chars" % len(params['meta'])) meta = (params['metakey'], params['meta']) else: meta = None offset = int(params['offset']) length = int(params['length']) paths = [p for p in filekey.split(os.path.sep) if p != ''] if len(paths) > 1: msg = "Bad request:"\ " filekey contains illegal path seperator tokens." loggerretr.debug(msg) request.setResponseCode(http.BAD_REQUEST, msg) return msg
return msg else: logger.info("received kFINDNODE request from %s..." % params['nodeID'][:10]) reqKu = {} reqKu['e'] = long(params['Ku_e']) reqKu['n'] = long(params['Ku_n']) reqKu = FludRSA.importPublicKey(reqKu) if reqKu.id() != params['nodeID']: request.setResponseCode(http.BAD_REQUEST, "Bad Identity") return "requesting node's ID and public key do not match" host = getCanonicalIP(request.getClientIP()) #return "{'id': '%s', 'k': %s}"\ # % (self.config.nodeID,\ # self.config.routing.findNode(fdecode(params['key']))) kclosest = self.config.routing.findNode(fdecode(key)) notclose = list(set(self.config.routing.knownExternalNodes()) - set(kclosest)) if len(notclose) > 0 and len(kclosest) > 1: r = random.choice(notclose) #logger.info("**** got some notclose: %s:%d ****" % (r[0],r[1])) kclosest.append(r) #logger.info("returning kFINDNODE response: %s" % kclosest) updateNode(self.node.client, self.config, host, int(params['port']), reqKu, params['nodeID']) return "{'id': '%s', 'k': %s}" % (self.config.nodeID, kclosest) class kSTORE_true(ROOT): # unrestricted kSTORE. Will store any key/value pair, as in generic # kademlia. This should be unregistered in FludServer (can't allow # generic stores).