def tryAuth(self, kind, user, data): """ Try to authenticate the user with the given method. Dispatches to a auth_* method. @param kind: the authentication method to try. @type kind: C{str} @param user: the username the client is authenticating with. @type user: C{str} @param data: authentication specific data sent by the client. @type data: C{str} @return: A Deferred called back if the method succeeded, or erred back if it failed. @rtype: C{defer.Deferred} """ log.msg('%s trying auth %s' % (user, kind)) if kind not in self.supportedAuthentications: return defer.fail( error.ConchError('unsupported authentication, failing')) kind = kind.replace('-', '_') f = getattr(self,'auth_%s'%kind, None) if f: ret = f(data) if not ret: return defer.fail( error.ConchError('%s return None instead of a Deferred' % kind)) else: return ret return defer.fail(error.ConchError('bad auth type: %s' % kind))
def _pamConv(self, items: List[Tuple[Any, int]]) -> defer.Deferred: """ Convert a list of PAM authentication questions into a MSG_USERAUTH_INFO_REQUEST. Returns a Deferred that will be called back when the user has responses to the questions. @param items: a list of 2-tuples (message, kind). We only care about kinds 1 (password) and 2 (text). @type items: C{list} @rtype: L{defer.Deferred} """ resp = [] for message, kind in items: if kind == 1: # Password resp.append((message, 0)) elif kind == 2: # Text resp.append((message, 1)) elif kind in (3, 4): return defer.fail( error.ConchError("cannot handle PAM 3 or 4 messages")) else: return defer.fail( error.ConchError("bad PAM auth kind %i" % (kind, ))) packet = NS(b"") + NS(b"") + NS(b"") packet += struct.pack(">L", len(resp)) for prompt, echo in resp: packet += NS(prompt) packet += bytes((echo, )) self.transport.sendPacket(userauth.MSG_USERAUTH_INFO_REQUEST, packet) # type: ignore self._pamDeferred = defer.Deferred() return self._pamDeferred
def _pamConv(self, items): """ Convert a list of PAM authentication questions into a MSG_USERAUTH_INFO_REQUEST. Returns a Deferred that will be called back when the user has responses to the questions. @param items: a list of 2-tuples (message, kind). We only care about kinds 1 (password) and 2 (text). @type items: C{list} @rtype: L{defer.Deferred} """ resp = [] for message, kind in items: if kind == 1: # password resp.append((message, 0)) elif kind == 2: # text resp.append((message, 1)) elif kind in (3, 4): return defer.fail(error.ConchError( 'cannot handle PAM 3 or 4 messages')) else: return defer.fail(error.ConchError( 'bad PAM auth kind %i' % kind)) packet = NS('') + NS('') + NS('') packet += struct.pack('>L', len(resp)) for prompt, echo in resp: packet += NS(prompt) packet += chr(echo) self.transport.sendPacket(MSG_USERAUTH_INFO_REQUEST, packet) self._pamDeferred = defer.Deferred() return self._pamDeferred
def tryAuth(self, kind, user, data): """ Try to authenticate the user with the given method. Dispatches to a auth_* method. @param kind: the authentication method to try. @type kind: L{bytes} @param user: the username the client is authenticating with. @type user: L{bytes} @param data: authentication specific data sent by the client. @type data: L{bytes} @return: A Deferred called back if the method succeeded, or erred back if it failed. @rtype: C{defer.Deferred} """ self._log.debug("{user!r} trying auth {kind!r}", user=user, kind=kind) if kind not in self.supportedAuthentications: return defer.fail(error.ConchError("unsupported authentication, failing")) kind = nativeString(kind.replace(b"-", b"_")) f = getattr(self, "auth_%s" % (kind,), None) if f: ret = f(data) if not ret: return defer.fail( error.ConchError("%s return None instead of a Deferred" % (kind,)) ) else: return ret return defer.fail(error.ConchError("bad auth type: %s" % (kind,)))
def requestAvatarId(self, credentials): print "requestAvatarId", credentials.username user = database.getUser(key=credentials.blob) if user: if not user['enabled']: return failure.Failure( error.ConchError("User account not enabled")) if not credentials.signature: return failure.Failure(error.ValidPublicKey()) pubKey = keys.Key.fromString(credentials.blob).keyObject if keys.verifySignature(pubKey, credentials.signature, credentials.sigData): print "login as %s" % credentials.username database.updateUserName(credentials.blob, credentials.username) return credentials.username else: return failure.Failure(error.ConchError("Incorrect signature")) elif config['ALLOW_ANNONYMOUS']: print "login as ANONYMOUS" user = "".join(Random().sample(string.letters, 30)) self.annons[user] = credentials.blob return user return failure.Failure(error.ConchError("Not allowed"))
def tryAuth(self, auth_type, ip, username, secret): auth_type = self._decode(auth_type, "Auth type") auth_type = auth_type.replace('-', '_') f = getattr(self, 'auth_%s' % (auth_type,), None) if f: ret = f(ip, username, secret) if not ret: return defer.fail( error.ConchError('%s return None instead of a Deferred' % (auth_type, ))) else: return ret return defer.fail(error.ConchError('bad auth type: %s' % (auth_type,)))
def requestAvatarId(self, creds): if creds.username in self.authKeys: userKey = self.authKeys[creds.username] if not creds.blob == base64.decodestring(userKey): raise failure.Failure(error.ConchError("Unrecognized key")) if not creds.signature: return failure.Failure(error.ValidPublicKey()) pubKey = keys.Key.fromString(data=creds.blob) if pubKey.verify(creds.signature, creds.sigData): return creds.username else: return failure.Failure(error.ConchError("Incorrect signature")) else: return failure.Failure(error.ConchError("No such user"))
def checkKey(self, credentials): validFlag, user = dissomniag.auth.User.loginManholeMethod( username=credentials.username, key=credentials.blob) if validFlag == LOGIN_SIGN.VALID_USER: return [True, user] elif validFlag == LOGIN_SIGN.NO_SUCH_USER: raise failure.Failure(error.ConchError("No such user")) elif validFlag == LOGIN_SIGN.UNVALID_ACCESS_METHOD: raise failure.Failure( error.ConchError("I don't recognize that key")) elif validFlag == LOGIN_SIGN.SECRET_UNVALID: raise failure.Failure( error.ConchError("I don't recognize that key")) else: raise failure.Failure(error.ConchError("Unspecified failure"))
def requestAvatarId(self, credentials): if self.authorizedKeys.has_key(credentials.username): userKey = self.authorizedKeys[credentials.username] if not credentials.blob == base64.decodestring(userKey): raise failure.failure( error.ConchError("I don't recognize that key")) if not credentials.signature: return failure.Failure(error.ValidPublicKey()) pubKey = keys.getPublicKeyObject(data=credentials.blob) if keys.verifySignature(pubKey, credentials.signature, credentials.sigData): return credentials.username else: return failure.Failure(error.ConchError("Incorrect signature")) else: return failure.Failure(error.ConchError("No such user"))
def verifyHostKey(self, pubKey, fingerprint): #d = defer.Deferred() #d.addCallback(lambda x:defer.succeed(1)) #d.callback(2) #return d goodKey = isInKnownHosts(options['host'], pubKey, {'known-hosts': None}) if goodKey == 1: # good key return defer.succeed(1) elif goodKey == 2: # AAHHHHH changed return defer.fail(error.ConchError('bad host key')) else: if options['host'] == self.transport.getPeer().host: host = options['host'] khHost = options['host'] else: host = '%s (%s)' % (options['host'], self.transport.getPeer().host) khHost = '%s,%s' % (options['host'], self.transport.getPeer().host) keyType = common.getNS(pubKey)[0] ques = """The authenticity of host '%s' can't be established.\r %s key fingerprint is %s.""" % (host, {b'ssh-dss':'DSA', b'ssh-rsa':'RSA'}[keyType], fingerprint) ques+='\r\nAre you sure you want to continue connecting (yes/no)? ' return deferredAskFrame(ques, 1).addCallback(self._cbVerifyHostKey, pubKey, khHost, keyType)
def verifyHostKey(self, pubKey, fingerprint): # d = defer.Deferred() # d.addCallback(lambda x:defer.succeed(1)) # d.callback(2) # return d goodKey = isInKnownHosts(options["host"], pubKey, {"known-hosts": None}) if goodKey == 1: # good key return defer.succeed(1) elif goodKey == 2: # AAHHHHH changed return defer.fail(error.ConchError("bad host key")) else: if options["host"] == self.transport.getPeer().host: host = options["host"] khHost = options["host"] else: host = "{} ({})".format(options["host"], self.transport.getPeer().host) khHost = "{},{}".format(options["host"], self.transport.getPeer().host) keyType = common.getNS(pubKey)[0] ques = """The authenticity of host '{}' can't be established.\r {} key fingerprint is {}.""".format( host, { b"ssh-dss": "DSA", b"ssh-rsa": "RSA" }[keyType], fingerprint, ) ques += "\r\nAre you sure you want to continue connecting (yes/no)? " return deferredAskFrame(ques, 1).addCallback(self._cbVerifyHostKey, pubKey, khHost, keyType)
def ssh_KEX_DH_GEX_REQUEST_OLD(self, packet): if self.ignoreNextPacket: self.ignoreNextPacket = 0 return if self.kexAlg == 'diffie-hellman-group1-sha1': # this is really KEXDH_INIT clientDHPubKey, foo = getMP(packet) y = Util.number.getRandomNumber(16, entropy.get_bytes) f = pow(DH_GENERATOR, y, DH_PRIME) sharedSecret = _MPpow(clientDHPubKey, y, DH_PRIME) h = sha.new() h.update(NS(self.otherVersionString)) h.update(NS(self.ourVersionString)) h.update(NS(self.clientKexInitPayload)) h.update(NS(self.ourKexInitPayload)) h.update(NS(self.factory.publicKeys[self.keyAlg])) h.update(MP(clientDHPubKey)) h.update(MP(f)) h.update(sharedSecret) exchangeHash = h.digest() self.sendPacket(MSG_KEXDH_REPLY, NS(self.factory.publicKeys[self.keyAlg])+ \ MP(f)+NS(keys.signData(self.factory.privateKeys[self.keyAlg], exchangeHash))) self._keySetup(sharedSecret, exchangeHash) elif self.kexAlg == 'diffie-hellman-group-exchange-sha1': self.kexAlg = 'diffie-hellman-group-exchange-sha1-old' self.ideal = struct.unpack('>L', packet)[0] self.g, self.p = self.factory.getDHPrime(self.ideal) self.sendPacket(MSG_KEX_DH_GEX_GROUP, MP(self.p) + MP(self.g)) else: raise error.ConchError('bad kexalg: %s' % self.kexAlg)
def ssh_USERAUTH_INFO_RESPONSE(self, packet: bytes) -> None: """ The user has responded with answers to PAMs authentication questions. Parse the packet into a PAM response and callback self._pamDeferred. Payload:: uint32 numer of responses string response 1 ... string response n """ d: Optional[defer.Deferred] = self._pamDeferred self._pamDeferred = None resp: List if not d: raise Exception( "can't find deferred in ssh_USERAUTH_INFO_RESPONSE") try: resp = [] numResps = struct.unpack(">L", packet[:4])[0] packet = packet[4:] while len(resp) < numResps: response, packet = getNS(packet) resp.append((response, 0)) if packet: raise error.ConchError(f"{len(packet):d} bytes of extra data") except Exception: d.errback(Failure()) else: d.callback(resp)
def requestAvatarId(self, credentials): _pubKey = keys.Key.fromString(credentials.blob) log.msg(format='public key attempt for user %(username)s with fingerprint %(fingerprint)s', eventid='KIPP0016', username=credentials.username, fingerprint=_pubKey.fingerprint()) return failure.Failure(error.ConchError('Incorrect signature'))
def startFactory(self): # disable coredumps if resource: resource.setrlimit(resource.RLIMIT_CORE, (0, 0)) else: log.msg('INSECURE: unable to disable core dumps.') if not hasattr(self, 'publicKeys'): self.publicKeys = self.getPublicKeys() for keyType, value in self.publicKeys.items(): if isinstance(value, str): warnings.warn( "Returning a mapping from strings to " "strings from getPublicKeys()/publicKeys (in %s) " "is deprecated. Return a mapping from " "strings to Key objects instead." % (qual(self.__class__)), DeprecationWarning, stacklevel=1) self.publicKeys[keyType] = keys.Key.fromString(value) if not hasattr(self, 'privateKeys'): self.privateKeys = self.getPrivateKeys() for keyType, value in self.privateKeys.items(): if not isinstance(value, keys.Key): warnings.warn("Returning a mapping from strings to " "PyCrypto key objects from " "getPrivateKeys()/privateKeys (in %s) " "is deprecated. Return a mapping from " "strings to Key objects instead." % (qual(self.__class__), ), DeprecationWarning, stacklevel=1) self.privateKeys[keyType] = keys.Key(value) if not self.publicKeys or not self.privateKeys: raise error.ConchError('no host keys, failing') if not hasattr(self, 'primes'): self.primes = self.getPrimes()
def execCommand(self, proto, cmd): self.cmd = cmd unittest.assert_( cmd.split()[0] in ['false', 'echo', 'secho', 'eecho', 'jumboliah'], 'invalid command: %s' % cmd.split()[0]) if cmd == 'jumboliah': raise error.ConchError('bad exec') self.proto = proto f = cmd.split()[0] if f == 'false': t = FalseTransport(proto) # Avoid disconnecting this immediately. If the channel is closed # before execCommand even returns the caller gets confused. reactor.callLater(0, t.loseConnection) elif f == 'echo': t = EchoTransport(proto) t.write(cmd[5:]) t.loseConnection() elif f == 'secho': t = SuperEchoTransport(proto) t.write(cmd[6:]) t.loseConnection() elif f == 'eecho': t = ErrEchoTransport(proto) t.write(cmd[6:]) t.loseConnection() self.avatar.conn.transport.expectedLoseConnection = 1
def ssh_USERAUTH_INFO_RESPONSE(self, packet): """ The user has responded with answers to PAMs authentication questions. Parse the packet into a PAM response and callback self._pamDeferred. Payload:: uint32 numer of responses string response 1 ... string response n """ d, self._pamDeferred = self._pamDeferred, None try: resp = [] numResps = struct.unpack('>L', packet[:4])[0] packet = packet[4:] while len(resp) < numResps: response, packet = getNS(packet) resp.append((response, 0)) if packet: raise error.ConchError("%i bytes of extra data" % len(packet)) except: d.errback(failure.Failure()) else: d.callback(resp)
def tryAuth(self, kind, user, data): log.msg('%s trying auth %s' % (user, kind)) if kind not in self.supportedAuthentications: return defer.fail( error.ConchError('unsupported authentication, failing')) kind = kind.replace('-', '_') f = getattr(self, 'auth_%s' % kind, None) if f: ret = f(data) if not ret: return defer.fail( error.ConchError('%s return None instead of a Deferred' % kind)) else: return ret return defer.fail(error.ConchError('bad auth type: %s' % kind))
def ssh_USERAUTH_REQUEST(self, packet): """ The client has requested authentication. Payload:: string user string next service string method <authentication specific data> @type packet: C{str} """ user, nextService, method, rest = getNS(packet, 3) if user != self.user or nextService != self.nextService: self.authenticatedWith = [] # clear auth state self.user = user self.nextService = nextService self.method = method d = self.tryAuth(method, user, rest) if not d: self._ebBadAuth( failure.Failure(error.ConchError('auth returned none'))) return d.addCallback(self._cbFinishedAuth) d.addErrback(self._ebMaybeBadAuth) d.addErrback(self._ebBadAuth) return d
def startFactory(self): """ Check for public and private keys. """ if not hasattr(self, 'publicKeys'): self.publicKeys = self.getPublicKeys() for keyType, value in self.publicKeys.items(): if isinstance(value, str): warnings.warn( "Returning a mapping from strings to " "strings from getPublicKeys()/publicKeys (in %s) " "is deprecated. Return a mapping from " "strings to Key objects instead." % (qual(self.__class__)), DeprecationWarning, stacklevel=1) self.publicKeys[keyType] = keys.Key.fromString(value) if not hasattr(self, 'privateKeys'): self.privateKeys = self.getPrivateKeys() for keyType, value in self.privateKeys.items(): if not isinstance(value, keys.Key): warnings.warn("Returning a mapping from strings to " "PyCrypto key objects from " "getPrivateKeys()/privateKeys (in %s) " "is deprecated. Return a mapping from " "strings to Key objects instead." % (qual(self.__class__), ), DeprecationWarning, stacklevel=1) self.privateKeys[keyType] = keys.Key(value) if not self.publicKeys or not self.privateKeys: raise error.ConchError('no host keys, failing') if not hasattr(self, 'primes'): self.primes = self.getPrimes()
def ssh_CHANNEL_OPEN_FAILURE(self, packet): localChannel, reasonCode = struct.unpack('>2L', packet[:8]) reasonDesc = common.getNS(packet[8:])[0] channel = self.channels[localChannel] del self.channels[localChannel] channel.conn = self reason = error.ConchError(reasonDesc, reasonCode) log.callWithLogger(channel, channel.openFailed, reason)
def verifyHostKey(self, pubKey, fingerprint): if self.fingerprint is not None and fingerprint != self.fingerprint: self.stateGood = False self.failure_reason = "fingerprint mismatch (received %s)" % fingerprint return defer.fail(error.ConchError('bad key')) else: self.stateGood = True return defer.succeed(1)
def verifyHostKey(self, public_key, fingerprint): unicode_fingerprint = fingerprint.decode('utf-8') if unicode_fingerprint in self.fingerprints: log.msg('verifyHostKey: fingerprint {} accepted'.format(unicode_fingerprint), system=LOG_SYSTEM) return defer.succeed(1) else: log.msg('verifyHostKey: fingerprint {} NOT accepted'.format(unicode_fingerprint), system=LOG_SYSTEM) return defer.fail(concherror.ConchError('Fingerprint not accepted'))
def ssh_REQUEST_FAILURE(self, packet): """ Our global request failed. Get the appropriate Deferred and errback it with the packet we received. """ log.msg('RF') self.deferreds['global'].pop(0).errback( error.ConchError('global request failed', packet))
def _cleanupGlobalDeferreds(self): """ All pending requests that have returned a deferred must be errbacked when this service is stopped, otherwise they might be left uncalled and uncallable. """ for d in self.deferreds["global"]: d.errback(error.ConchError("Connection stopped.")) del self.deferreds["global"][:]
def auth_publickey(self, packet): """ We subclass to intercept non-dsa/rsa keys, or Conch will crash on ecdsa.. """ algName, blob, rest = getNS(packet[1:], 2) if not algName in (b'ssh-rsa', b'ssh-dsa'): log.msg("Attempted public key authentication with {} algorithm".format(algName)) return defer.fail(error.ConchError("Incorrect signature")) return userauth.SSHUserAuthServer.auth_publickey(self, packet)
def requestAvatarId(self, credentials): userKeyString = self.authorizedKeys.get(credentials.username) if not userKeyString: return failure.Failure(error.ConchError("No such user")) # Remove the 'ssh-rsa' type before decoding. if credentials.blob != base64.decodestring( userKeyString.split(" ")[1]): raise failure.failure( error.ConchError("I don't recognize that key")) if not credentials.signature: return failure.Failure(error.ValidPublicKey()) userKey = keys.Key.fromString(data=userKeyString) if userKey.verify(credentials.signature, credentials.sigData): return credentials.username else: print "signature check failed" return failure.Failure(error.ConchError("Incorrect signature"))
def ssh_KEX_DH_GEX_REQUEST_OLD(self, packet): """ This represents two different key exchange methods that share the same integer value. KEXDH_INIT (for diffie-hellman-group1-sha1 exchanges) payload:: integer e (the client's Diffie-Hellman public key) We send the KEXDH_REPLY with our host key and signature. KEX_DH_GEX_REQUEST_OLD (for diffie-hellman-group-exchange-sha1) payload:: integer ideal (ideal size for the Diffie-Hellman prime) We send the KEX_DH_GEX_GROUP message with the group that is closest in size to ideal. If we were told to ignore the next key exchange packet by ssh_KEXINIT, drop it on the floor and return. """ if self.ignoreNextPacket: self.ignoreNextPacket = 0 return if self.kexAlg == 'diffie-hellman-group1-sha1': # this is really KEXDH_INIT clientDHpublicKey, foo = getMP(packet) y = Util.number.getRandomNumber(512, randbytes.secureRandom) serverDHpublicKey = _MPpow(DH_GENERATOR, y, DH_PRIME) sharedSecret = _MPpow(clientDHpublicKey, y, DH_PRIME) h = sha1() h.update(NS(self.otherVersionString)) h.update(NS(self.ourVersionString)) h.update(NS(self.otherKexInitPayload)) h.update(NS(self.ourKexInitPayload)) h.update(NS(self.factory.publicKeys[self.keyAlg].blob())) h.update(MP(clientDHpublicKey)) h.update(serverDHpublicKey) h.update(sharedSecret) exchangeHash = h.digest() self.sendPacket( MSG_KEXDH_REPLY, NS(self.factory.publicKeys[self.keyAlg].blob()) + serverDHpublicKey + NS(self.factory.privateKeys[self.keyAlg].sign(exchangeHash))) self._keySetup(sharedSecret, exchangeHash) elif self.kexAlg == 'diffie-hellman-group-exchange-sha1': self.dhGexRequest = packet ideal = struct.unpack('>L', packet)[0] self.g, self.p = self.factory.getDHPrime(ideal) self.sendPacket(MSG_KEX_DH_GEX_GROUP, MP(self.p) + MP(self.g)) else: raise error.ConchError('bad kexalg: %s' % self.kexAlg)
def _pamConv(self, items): resp = [] for message, kind in items: if kind == 1: # password resp.append((message, 0)) elif kind == 2: # text resp.append((message, 1)) elif kind in (3, 4): return defer.fail( error.ConchError('cannot handle PAM 3 or 4 messages')) else: return defer.fail( error.ConchError('bad PAM auth kind %i' % kind)) packet = NS('') + NS('') + NS('') packet += struct.pack('>L', len(resp)) for prompt, echo in resp: packet += NS(prompt) packet += chr(echo) self.transport.sendPacket(MSG_USERAUTH_INFO_REQUEST, packet) self._pamDeferred = defer.Deferred() return self._pamDeferred
def requestAvatarId(self, credentials): log.msg(credentials) user_key_string = self.authorizedKeys.get(credentials.username) if not user_key_string: return failure.Failure(error.ConchError('No such user')) # strip ssh-rsa type before decoding decoded_string = base64.decodestring(user_key_string.split(' ')[1]) if decoded_string != credentials.blob: raise failure.Failure( error.ConchError('I don\'t recognize this key')) if not credentials.signature: raise error.ValidPublicKey() user_key = keys.Key.fromString(data=user_key_string) if user_key.verify(credentials.signature, credentials.sigData): return credentials.username else: log.err('Signature check failed!') return failure.Failure(error.ConchError('Incorrect signature'))