예제 #1
0
 protected function createResponse(\fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context, $hostAlgo)
 {
     $kexAlgo = $context['kexAlgo'];
     $kexAlgo = new $kexAlgo();
     $message = \fpoirotte\Pssht\Messages\KEX\ECDH\INIT\Curve25519::unserialize($decoder);
     return new \fpoirotte\Pssht\Messages\KEX\ECDH\REPLY\Curve25519($message, $context['serverKeys'][$hostAlgo], $transport->getEncryptor(), $kexAlgo, $context['kex']['server'], $context['kex']['client'], $context['identity']['server'], $context['identity']['client']);
 }
예제 #2
0
 public function authenticate(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base $message, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     if (!$message instanceof \fpoirotte\Pssht\Messages\USERAUTH\REQUEST\PublicKey) {
         throw new \InvalidArgumentException();
     }
     if ($message->getSignature() === null) {
         return self::AUTH_REJECT;
     }
     $logging = \Plop\Plop::getInstance();
     $reverse = gethostbyaddr($transport->getAddress());
     $algos = \fpoirotte\Pssht\Algorithms::factory();
     $cls = $algos->getClass('PublicKey', $message->getAlgorithm());
     if ($cls === null || !$this->store->exists($message->getUserName(), $message->getKey())) {
         $logging->info('Rejected public key connection from remote host "%(reverse)s" ' . 'to "%(luser)s" (unsupported key)', array('luser' => escape($message->getUserName()), 'reverse' => $reverse));
         return self::AUTH_REJECT;
     }
     $key = $cls::loadPublic(base64_encode($message->getKey()));
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeString($context['DH']->getExchangeHash());
     $encoder->encodeBytes(chr(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base::getMessageId()));
     $encoder->encodeString($message->getUserName());
     $encoder->encodeString($message->getServiceName());
     $encoder->encodeString(static::getName());
     $encoder->encodeBoolean(true);
     $encoder->encodeString($message->getAlgorithm());
     $encoder->encodeString($message->getKey());
     if ($key->check($encoder->getBuffer()->get(0), $message->getSignature())) {
         $logging->info('Accepted public key connection from remote host "%(reverse)s" ' . 'to "%(luser)s" (using "%(algorithm)s" algorithm)', array('luser' => escape($message->getUserName()), 'reverse' => $reverse, 'algorithm' => escape($message->getAlgorithm())));
         return self::AUTH_ACCEPT;
     }
     $logging->info('Rejected public key connection from remote host "%(reverse)s" ' . 'to "%(luser)s" (invalid signature)', array('luser' => escape($message->getUserName()), 'reverse' => $reverse));
     return self::AUTH_REJECT;
 }
예제 #3
0
 /**
  * Construct a new SSH connection layer.
  *
  *  \param fpoirotte::Pssht::Transport $transport
  *      SSH transport layer.
  */
 public function __construct(\fpoirotte\Pssht\Transport $transport)
 {
     $this->channels = array();
     $transport->setHandler(\fpoirotte\Pssht\Messages\CHANNEL\OPEN::getMessageId(), new \fpoirotte\Pssht\Handlers\CHANNEL\OPEN($this))->setHandler(\fpoirotte\Pssht\Messages\CHANNEL\CLOSE::getMessageId(), new \fpoirotte\Pssht\Handlers\CHANNEL\CLOSE($this))->setHandler(\fpoirotte\Pssht\Messages\CHANNEL\REQUEST\Base::getMessageId(), new \fpoirotte\Pssht\Handlers\CHANNEL\REQUEST($this));
     foreach (array_merge(range(91, 96), array(99, 100)) as $msgId) {
         $transport->setHandler($msgId, $this);
     }
 }
예제 #4
0
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $message = \fpoirotte\Pssht\Messages\CHANNEL\DATA::unserialize($decoder);
     $channel = $message->getChannel();
     $response = new \fpoirotte\Pssht\Messages\CHANNEL\DATA($channel, $message->getData());
     $transport->writeMessage($response);
     return true;
 }
예제 #5
0
파일: CLOSE.php 프로젝트: fpoirotte/pssht
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $message = \fpoirotte\Pssht\Messages\CHANNEL\CLOSE::unserialize($decoder);
     $channel = $message->getChannel();
     $response = new \fpoirotte\Pssht\Messages\CHANNEL\CLOSE($this->connection->getChannel($channel));
     $transport->writeMessage($response);
     $this->connection->freeChannel($channel);
     return true;
 }
예제 #6
0
파일: RFC5656.php 프로젝트: fpoirotte/pssht
 protected function createResponse(\fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context, $hostAlgo)
 {
     $kexAlgo = $context['kexAlgo'];
     $kexAlgo = new $kexAlgo();
     $curveName = str_replace('ecdh-sha2-', '', $kexAlgo::getName());
     $cls = str_replace('nist', 'NIST', $curveName);
     $cls = "\\fpoirotte\\Pssht\\Messages\\KEX\\ECDH\\INIT\\{$cls}";
     $message = $cls::unserialize($decoder);
     $curve = \fpoirotte\Pssht\ECC\Curve::getCurve($curveName);
     return new \fpoirotte\Pssht\Messages\KEX\ECDH\REPLY\RFC5656($curve, $message, $context['serverKeys'][$hostAlgo], $transport->getEncryptor(), $kexAlgo, $context['kex']['server'], $context['kex']['client'], $context['identity']['server'], $context['identity']['client']);
 }
예제 #7
0
파일: OPEN.php 프로젝트: fpoirotte/pssht
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $message = \fpoirotte\Pssht\Messages\CHANNEL\OPEN::unserialize($decoder);
     $recipientChannel = $message->getChannel();
     if ($message->getType() === 'session') {
         $response = new \fpoirotte\Pssht\Messages\CHANNEL\OPEN\CONFIRMATION($recipientChannel, $this->connection->allocateChannel($message), 0x200000, 0x800000);
     } else {
         $response = new \fpoirotte\Pssht\Messages\CHANNEL\OPEN\FAILURE($recipientChannel, \fpoirotte\Pssht\Messages\CHANNEL\OPEN\FAILURE::SSH_OPEN_UNKNOWN_CHANNEL_TYPE, 'No such channel type');
     }
     $transport->writeMessage($response);
     return true;
 }
예제 #8
0
파일: REQUEST.php 프로젝트: fpoirotte/pssht
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $message = \fpoirotte\Pssht\Messages\SERVICE\REQUEST::unserialize($decoder);
     $service = $message->getServiceName();
     if ($service === 'ssh-userauth') {
         $response = new \fpoirotte\Pssht\Messages\SERVICE\ACCEPT($service);
         $transport->setHandler(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base::getMessageId(), $this->userAuthRequestHandler);
     } else {
         $response = new DISCONNECT(DISCONNECT::SSH_DISCONNECT_SERVICE_NOT_AVAILABLE, 'No such service');
     }
     $transport->writeMessage($response);
     return true;
 }
예제 #9
0
 public function authenticate(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base $message, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     if (!$message instanceof \fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Password) {
         throw new \InvalidArgumentException();
     }
     $logging = \Plop\Plop::getInstance();
     $reverse = gethostbyaddr($transport->getAddress());
     if (isset($this->credentials[$message->getUserName()]) && $message->getPassword() === $this->credentials[$message->getUserName()]) {
         $logging->info('Accepted password connection from ' . 'remote host "%(reverse)s" to "%(luser)s"', array('reverse' => $reverse, 'luser' => escape($message->getUserName())));
         return self::AUTH_ACCEPT;
     }
     $logging->info('Rejected password connection from ' . 'remote host "%(reverse)s" to "%(luser)s" ' . '(invalid credentials)', array('reverse' => $reverse, 'luser' => escape($message->getUserName())));
     return self::AUTH_REJECT;
 }
예제 #10
0
파일: INIT.php 프로젝트: fpoirotte/pssht
 protected function createResponse(\fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context, $hostAlgo)
 {
     $kexAlgo = $context['kexAlgo'];
     $kexAlgo = new $kexAlgo();
     $message = \fpoirotte\Pssht\Messages\KEXDH\INIT::unserialize($decoder);
     /*
             // @TODO: we ought to check whether the given public key is valid.
             //
             // Unfortunately, the current API is broken as getQ() only exists
             // for ECDH. Also, even though the regular DH has a getE() method,
             // it returns raw GMP resources/objects which are unusable here.
             if (!$message->getQ()->isValid()) {
                 throw new \InvalidArgumentException();
             }
     */
     return new \fpoirotte\Pssht\Messages\KEXDH\REPLY($message, $context['serverKeys'][$hostAlgo], $transport->getEncryptor(), $kexAlgo, $context['kex']['server'], $context['kex']['client'], $context['identity']['server'], $context['identity']['client']);
 }
예제 #11
0
파일: REQUEST.php 프로젝트: fpoirotte/pssht
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $channel = $decoder->decodeUint32();
     $type = $decoder->decodeString();
     $wantsReply = $decoder->decodeBoolean();
     $encoder->encodeUint32($channel);
     $encoder->encodeString($type);
     $encoder->encodeBoolean($wantsReply);
     $decoder->getBuffer()->unget($encoder->getBuffer()->get(0));
     $remoteChannel = $this->connection->getChannel($channel);
     switch ($type) {
         case 'exec':
         case 'shell':
         case 'pty-req':
             // Normalize the name.
             // Eg. "pty-req" becomes "PtyReq".
             $cls = str_replace(' ', '', ucwords(str_replace('-', ' ', $type)));
             $cls = '\\fpoirotte\\Pssht\\Messages\\CHANNEL\\REQUEST\\' . $cls;
             $message = $cls::unserialize($decoder);
             break;
         default:
             if ($wantsReply) {
                 $response = new \fpoirotte\Pssht\Messages\CHANNEL\FAILURE($remoteChannel);
                 $transport->writeMessage($response);
             }
             return true;
     }
     if (!$wantsReply) {
         return true;
     }
     if (in_array($type, array('shell', 'exec'), true)) {
         $response = new \fpoirotte\Pssht\Messages\CHANNEL\SUCCESS($remoteChannel);
     } else {
         $response = new \fpoirotte\Pssht\Messages\CHANNEL\FAILURE($remoteChannel);
     }
     $transport->writeMessage($response);
     if (in_array($type, array('shell', 'exec'), true)) {
         $callable = $transport->getApplicationFactory();
         if ($callable !== null) {
             call_user_func($callable, $transport, $this->connection, $message);
         }
     }
     return true;
 }
예제 #12
0
파일: NEWKEYS.php 프로젝트: fpoirotte/pssht
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     unset($context['rekeying']);
     $response = new \fpoirotte\Pssht\Messages\NEWKEYS();
     $transport->writeMessage($response);
     $logging = \Plop\Plop::getInstance();
     // Reset the various keys.
     $kexAlgo = $context['kexAlgo'];
     $kexAlgo = new $kexAlgo();
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeMpint($context['DH']->getSharedSecret());
     $sharedSecret = $encoder->getBuffer()->get(0);
     $exchangeHash = $context['DH']->getExchangeHash();
     $sessionId = $context['sessionIdentifier'];
     $limiters = array('A' => array($context['C2S']['Encryption'], 'getIVSize'), 'B' => array($context['S2C']['Encryption'], 'getIVSize'), 'C' => array($context['C2S']['Encryption'], 'getKeySize'), 'D' => array($context['S2C']['Encryption'], 'getKeySize'), 'E' => array($context['C2S']['MAC'], 'getKeySize'), 'F' => array($context['C2S']['MAC'], 'getKeySize'));
     $shared = gmp_strval($context['DH']->getSharedSecret(), 16);
     $shared = str_pad($shared, strlen($shared) + 1 >> 1 << 1, '0', STR_PAD_LEFT);
     $logging->debug('Key exchange: %s', array($context['kexAlgo']));
     $logging->debug('Shared secret: %s', array(wordwrap($shared, 16, ' ', true)));
     $logging->debug('Hash: %s', array(wordwrap(bin2hex($exchangeHash), 16, ' ', true)));
     foreach (array('A', 'B', 'C', 'D', 'E', 'F') as $keyIndex) {
         $key = $kexAlgo->hash($sharedSecret . $exchangeHash . $keyIndex . $sessionId);
         $limit = call_user_func($limiters[$keyIndex]);
         while (strlen($key) < $limit) {
             $key .= $kexAlgo->hash($sharedSecret . $exchangeHash . $key);
         }
         $key = (string) substr($key, 0, $limit);
         $context['keys'][$keyIndex] = $key;
         $logging->debug('Key %(keyName)s: %(keyValue)s', array('keyName' => $keyIndex, 'keyValue' => wordwrap(bin2hex($key), 16, ' ', true)));
     }
     // Encryption
     $cls = $context['C2S']['Encryption'];
     $transport->setDecryptor(new $cls($context['keys']['A'], $context['keys']['C']));
     $logging->debug('C2S Encryption: %s', array($cls));
     $cls = $context['S2C']['Encryption'];
     $transport->setEncryptor(new $cls($context['keys']['B'], $context['keys']['D']));
     $logging->debug('S2C Encryption: %s', array($cls));
     // MAC
     $cls = $context['C2S']['MAC'];
     $transport->setInputMAC(new $cls($context['keys']['E']));
     $logging->debug('C2S MAC: %s', array($cls));
     $cls = $context['S2C']['MAC'];
     $transport->setOutputMAC(new $cls($context['keys']['F']));
     $logging->debug('S2C MAC: %s', array($cls));
     // Compression
     $cls = $context['C2S']['Compression'];
     $transport->setUncompressor(new $cls(CompressionInterface::MODE_UNCOMPRESS));
     $logging->debug('C2S Compression: %s', array($cls));
     $cls = $context['S2C']['Compression'];
     $transport->setCompressor(new $cls(CompressionInterface::MODE_COMPRESS));
     $logging->debug('S2C Compression: %s', array($cls));
     return true;
 }
예제 #13
0
 public function authenticate(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base $message, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     if (!$message instanceof \fpoirotte\Pssht\Messages\USERAUTH\REQUEST\HostBased) {
         throw new \InvalidArgumentException();
     }
     $logging = \Plop\Plop::getInstance();
     $reverse = gethostbyaddr($transport->getAddress());
     $untrustedHost = rtrim($message->getHostname(), '.');
     $algos = \fpoirotte\Pssht\Algorithms::factory();
     $cls = $algos->getClass('PublicKey', $message->getAlgorithm());
     if ($cls === null || !$this->store->exists($message->getUserName(), $message->getKey())) {
         $logging->info('Rejected host based connection from %(ruser)s@%(rhost)s ' . '(%(ruser)s@%(reverse)s) to "%(luser)s" ' . '(unsupported key)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse));
         return self::AUTH_REMOVE;
     }
     $key = $cls::loadPublic(base64_encode($message->getKey()));
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeString($context['DH']->getExchangeHash());
     $encoder->encodeBytes(chr(\fpoirotte\Pssht\Messages\USERAUTH\REQUEST\Base::getMessageId()));
     $encoder->encodeString($message->getUserName());
     $encoder->encodeString($message->getServiceName());
     $encoder->encodeString(static::getName());
     $encoder->encodeString($message->getAlgorithm());
     $encoder->encodeString($message->getKey());
     $encoder->encodeString($message->getHostname());
     $encoder->encodeString($message->getRemoteUser());
     if (!$key->check($encoder->getBuffer()->get(0), $message->getSignature())) {
         $logging->warn('Rejected host based connection from %(ruser)s@%(rhost)s ' . '(%(ruser)s@%(reverse)s) to "%(luser)s" (invalid signature)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse));
         return self::AUTH_REJECT;
     }
     if ($reverse !== $untrustedHost) {
         $logging->warning('Ignored reverse lookup mismatch for %(address)s (' . '"%(reverse)s" vs. "%(untrusted)s")', array('address' => $transport->getAddress(), 'reverse' => $reverse, 'untrusted' => escape($untrustedHost)));
     }
     if ($message->getUserName() !== $message->getRemoteUser()) {
         $logging->warning('Rejected host based connection from %(ruser)s@%(rhost)s ' . '(%(ruser)s@%(reverse)s): remote user does not match ' . 'local user (%(luser)s)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse));
         return self::AUTH_REMOVE;
     }
     $logging->info('Accepted host based connection ' . 'from "%(ruser)s@%(rhost)s" (%(ruser)s@%(reverse)s) ' . 'to "%(luser)s" (using "%(algorithm)s" algorithm)', array('ruser' => escape($message->getRemoteUser()), 'luser' => escape($message->getUserName()), 'rhost' => escape($untrustedHost), 'reverse' => $reverse, 'algorithm' => escape($message->getAlgorithm())));
     return self::AUTH_ACCEPT;
 }
예제 #14
0
 public function handleKEXINIT(\fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $algos = \fpoirotte\Pssht\Algorithms::factory();
     // Cookie
     $random = new \fpoirotte\Pssht\Random\OpenSSL();
     // KEX
     $kexAlgos = $algos->getAlgorithms('KEX');
     if (!count($kexAlgos)) {
         throw new \RuntimeException();
     }
     // Server key
     $serverHostKeyAlgos = array_intersect($algos->getAlgorithms('PublicKey'), array_keys($context['serverKeys']));
     if (!count($serverHostKeyAlgos)) {
         throw new \RuntimeException();
     }
     // Encryption
     $encAlgosC2S = array_diff($algos->getAlgorithms('Encryption'), array('none'));
     $encAlgosS2C = $encAlgosC2S;
     if (!count($encAlgosC2S)) {
         throw new \RuntimeException();
     }
     // MAC
     $macAlgosC2S = array_diff($algos->getAlgorithms('MAC'), array('none'));
     $macAlgosS2C = $macAlgosC2S;
     if (!count($macAlgosC2S)) {
         throw new \RuntimeException();
     }
     // Compression
     $compAlgosC2S = $algos->getAlgorithms('Compression');
     $compAlgosS2C = $compAlgosC2S;
     if (!count($compAlgosC2S)) {
         throw new \RuntimeException();
     }
     $kex = new \fpoirotte\Pssht\Messages\KEXINIT($random, $kexAlgos, $serverHostKeyAlgos, $encAlgosC2S, $encAlgosS2C, $macAlgosC2S, $macAlgosS2C, $compAlgosC2S, $compAlgosS2C);
     $context['kex']['server'] = $kex;
     $transport->writeMessage($kex);
     return true;
 }
예제 #15
0
 public static function addHandlers(\fpoirotte\Pssht\Transport $transport)
 {
     $transport->setHandler(\fpoirotte\Pssht\Messages\KEX\ECDH\INIT\Curve25519::getMessageId(), new \fpoirotte\Pssht\Handlers\KEX\ECDH\INIT\Curve25519());
 }
예제 #16
0
파일: REQUEST.php 프로젝트: fpoirotte/pssht
 /**
  * Report an authentication failure.
  *
  *  \param fpoirotte::Pssht::Transport $transport
  *      Transport layer used to report the failure.
  *
  *  \param array &$context
  *      SSH session context (containing authentication methods
  *      that may continue).
  *
  *  \param bool $partial
  *      (optional) Indicates whether the request ended with
  *      a partial success (\b true) or not (\b false).
  *      If omitted, \b false is implied.
  *
  *  \retval true
  *      This method always returns true.
  */
 protected function failure(\fpoirotte\Pssht\Transport $transport, array &$context, $partial = false)
 {
     if (!is_bool($partial)) {
         throw new \InvalidArgumentException();
     }
     $remaining = $context['authMethods'];
     unset($remaining['none']);
     $remaining = array_keys($remaining);
     $response = new \fpoirotte\Pssht\Messages\USERAUTH\FAILURE($remaining, $partial);
     $transport->writeMessage($response);
     return true;
 }