def __init__(self, string=None, fname=None, validatedDigests=None, _keepServerContents=0): """DOCDOC raises ConfigError. """ if string: contents = string else: try: contents = readPossiblyGzippedFile(fname) except (IOError, zlib.error), e: raise ConfigError("Couldn't decompress %s: %s" % (fname, e))
def __init__(self, string=None, fname=None, validatedDigests=None): """Create a new ServerDirectory object, either from a literal <string> (if specified) or a filename [possibly gzipped]. If validatedDigests is provided, it must be a dict whose keys are the digests of already-validated descriptors. Any descriptor whose (calculated) digest matches doesn't need to be validated again. """ if string: contents = string else: try: contents = readPossiblyGzippedFile(fname) except (IOError, zlib.error), e: raise ConfigError("Couldn't decompress %s: %s" % (fname, e))
def parseDirectory(fname, validatedDigests=None): """DOCDOC""" try: s = readPossiblyGzippedFile(fname) except (IOError, zlib.error), e: raise ConfigError("Couldn't decompress %s: %s" % (fname, e))
def validate(self, lines, contents): #### # Check 'Server' section. server = self['Server'] if server['Descriptor-Version'] != '0.2': raise ConfigError("Unrecognized descriptor version %r" % server['Descriptor-Version']) #### # Check the digest of file digest = getServerInfoDigest(contents) if digest != server['Digest']: raise ConfigError("Invalid digest") # Have we already validated this particular ServerInfo? if (self._validatedDigests and self._validatedDigests.has_key(digest)): self._isValidated = 1 return # Validate the rest of the server section. identityKey = server['Identity'] identityBytes = identityKey.get_modulus_bytes() if not (MIN_IDENTITY_BYTES <= identityBytes <= MAX_IDENTITY_BYTES): raise ConfigError("Invalid length on identity key") if server['Published'] > time.time() + 600: raise ConfigError("Server published in the future") if server['Valid-Until'] <= server['Valid-After']: raise ConfigError("Server is never valid") if server['Contact'] and len(server['Contact']) > MAX_CONTACT: raise ConfigError("Contact too long") if server['Comments'] and len(server['Comments']) > MAX_COMMENTS: raise ConfigError("Comments too long") if server['Contact-Fingerprint'] and \ len(server['Contact-Fingerprint']) > MAX_FINGERPRINT: raise ConfigError("Contact-Fingerprint too long") packetKeyBytes = server['Packet-Key'].get_modulus_bytes() if packetKeyBytes != PACKET_KEY_BYTES: raise ConfigError("Invalid length on packet key") #### # Check signature try: signedDigest = pk_check_signature(server['Signature'], identityKey) except CryptoError: raise ConfigError("Invalid signature") if digest != signedDigest: raise ConfigError("Signed digest is incorrect") ## Incoming/MMTP section inMMTP = self['Incoming/MMTP'] if inMMTP: if inMMTP['Version'] != '0.1': raise ConfigError("Unrecognized MMTP descriptor version %s" % inMMTP['Version']) ## Outgoing/MMTP section outMMTP = self['Outgoing/MMTP'] if outMMTP: if outMMTP['Version'] != '0.1': raise ConfigError("Unrecognized MMTP descriptor version %s" % inMMTP['Version']) # FFFF When a better client module system exists, check the # FFFF module descriptors. self._isValidated = 1
def validate(self, lines, contents): def _haveEntry(self, section, ent): entries = self._sectionEntries return len([e for e in entries[section] if e[0] == ent]) != 0 # Preemptively configure the log before validation, so we don't # write to the terminal if we've been asked not to. if not self['Server'].get("EchoMessages", 0): LOG.handlers = [] # ???? This can't be the best way to do this. # Now, validate the host section. mixminion.Config._validateHostSection(self['Host']) # Server section server = self['Server'] bits = server['IdentityKeyBits'] if not (2048 <= bits <= 4096): raise ConfigError("IdentityKeyBits must be between 2048 and 4096") if server['EncryptIdentityKey']: LOG.warn("Identity key encryption not yet implemented") if server['EncryptPrivateKey']: LOG.warn("Encrypted private keys not yet implemented") if server['PublicKeyLifetime'].getSeconds() < 24 * 60 * 60: raise ConfigError("PublicKeyLifetime must be at least 1 day.") if server['PublicKeyOverlap'].getSeconds() < 6 * 60 * 60: raise ConfigError("PublicKeyOverlap must be >= 6 hours") if server['PublicKeyOverlap'].getSeconds() > 72 * 60 * 60: raise ConfigError("PublicKeyOverlap must be <= 72 hours") if _haveEntry(self, 'Server', 'Mode'): LOG.warn("Mode specification is not yet supported.") mixInterval = server['MixInterval'].getSeconds() if mixInterval < 30 * 60: LOG.warn("Dangerously low MixInterval") if server['MixAlgorithm'] == 'TimedMixPool': if _haveEntry(self, 'Server', 'MixPoolRate'): LOG.warn("Option MixPoolRate is not used for Timed mixing.") if _haveEntry(self, 'Server', 'MixPoolMinSize'): LOG.warn("Option MixPoolMinSize is not used for Timed mixing.") else: rate = server['MixPoolRate'] minSize = server['MixPoolMinSize'] if rate < 0.05: LOG.warn("Unusually low MixPoolRate %s", rate) if minSize < 0: raise ConfigError("MixPoolMinSize %s must be nonnegative.") if not self['Incoming/MMTP'].get('Enabled'): LOG.warn("Disabling incoming MMTP is not yet supported.") if [ e for e in self._sectionEntries['Incoming/MMTP'] if e[0] in ('Allow', 'Deny') ]: LOG.warn("Allow/deny are not yet supported") if not self['Outgoing/MMTP'].get('Enabled'): LOG.warn("Disabling outgoing MMTP is not yet supported.") if [ e for e in self._sectionEntries['Outgoing/MMTP'] if e[0] in ('Allow', 'Deny') ]: LOG.warn("Allow/deny are not yet supported") mc = self['Outgoing/MMTP'].get('MaxConnections') if mc is not None and mc < 1: raise ConfigError("MaxConnections must be at least 1.") bw = self['Outgoing/MMTP'].get('MaxBandwidth') if bw is not None and bw < 4096: #XXXX007 this is completely arbitrary. :P raise ConfigError("MaxBandwidth must be at least 4KB.") self.validateRetrySchedule("Outgoing/MMTP") self.moduleManager.validate(self, lines, contents)