Esempio n. 1
0
 public function log($level, $message, array $context = array())
 {
     /***
      * \note
      *      There are several contexts in which the follow code
      *      may not work as expected.
      *
      *      Using threads (https://github.com/krakjoe/pthreads)
      *      with this method will probably mess things up.
      *
      *      The following functions may also cause trouble
      *      depending on how they're used:
      *      - register_tick_function()
      *      - debug_backtrace()
      */
     $logging = \Plop\Plop::getInstance();
     $factory = $logging->getRecordFactory();
     try {
         // Switch to a factory that uses PSR3-interpolation.
         $logging->setRecordFactory(static::$factory);
         $logging->{$level}($message, $context);
     } catch (Exception $e) {
     }
     // Restore the original factory.
     $logging->setRecordFactory($factory);
 }
Esempio n. 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;
 }
Esempio n. 3
0
 /**
  * Called after the bot has finished its execution
  * to perform cleanup tasks.
  *
  * \param resource $handle
  *      Open file handle on the pidfile.
  *
  * \param string $pidfile
  *      Name of the pidfile.
  *
  * \return
  *      This method does not return anything.
  */
 public static function cleanupPidfile($handle, $pidfile)
 {
     flock($handle, LOCK_UN);
     @unlink($pidfile);
     $logger = \Plop\Plop::getInstance();
     $logger->debug('Removed lock on pidfile (%(pidfile)s)', array('pidfile' => $pidfile));
 }
Esempio n. 4
0
 public function __construct($cipher, $key, $taglen)
 {
     $logging = \Plop\Plop::getInstance();
     $this->cipher = mcrypt_module_open($cipher, null, 'ecb', null);
     mcrypt_generic_init($this->cipher, $key, str_repeat("", 16));
     $this->taglen = $taglen;
     $logging->debug('Pre-computing GCM table');
     $H = gmp_init(bin2hex(mcrypt_generic($this->cipher, str_repeat("", 16))), 16);
     $H = str_pad(gmp_strval($H, 2), 128, '0', STR_PAD_LEFT);
     $R = gmp_init('E1000000000000000000000000000000', 16);
     $this->table = array();
     for ($i = 0; $i < 16; $i++) {
         $this->table[$i] = array();
         for ($j = 0; $j < 256; $j++) {
             $V = gmp_init(dechex($j) . str_repeat("00", $i), 16);
             $Z = gmp_init(0);
             for ($k = 0; $k < 128; $k++) {
                 // Compute Z_n+1
                 if ($H[$k]) {
                     $Z = gmp_xor($Z, $V);
                 }
                 // Compute V_n+1
                 $odd = gmp_testbit($V, 0);
                 $V = gmp_div_q($V, 2);
                 if ($odd) {
                     $V = gmp_xor($V, $R);
                 }
             }
             $this->table[$i][$j] = pack('H*', str_pad(gmp_strval($Z, 16), 32, 0, STR_PAD_LEFT));
         }
     }
     $logging->debug('Done pre-computing GCM table');
 }
Esempio n. 5
0
File: Plop.php Progetto: erebot/plop
 public function __construct($empty)
 {
     parent::__construct();
     $this->created = 12345678.9;
     if ($empty) {
         $this->loggers = array();
     }
 }
Esempio n. 6
0
File: Base.php Progetto: erebot/api
 /**
  * Create the base for a proxy client.
  *
  * \param stream $socket
  *      A socket which will be used to tunnel data.
  */
 public function __construct($socket)
 {
     if (!is_resource($socket)) {
         throw new \Erebot\InvalidValueException('Not a socket');
     }
     $this->socket = $socket;
     $logging = \Plop\Plop::getInstance();
     $this->logger = $logging->getLogger(__FILE__ . DIRECTORY_SEPARATOR);
 }
Esempio n. 7
0
 /**
  * @dataProvider    vectors
  * @group           medium
  */
 public function testED25519($index, $private, $public, $message, $signature)
 {
     $logging = \Plop\Plop::getInstance();
     $logging->debug("Testing using vector #%d", array($index));
     $msg = pack('H*', $message);
     $key = new ED25519TestHelper(pack('H*', $public), pack('H*', $private));
     $result = $key->sign($msg);
     $this->assertSame($signature, bin2hex($result));
     $this->assertTrue($key->check($msg, $result));
 }
Esempio n. 8
0
 /**
  * Construct a new instance of this filter.
  *
  * \param int|string $level
  *      Name or value of the minimal level that this filter
  *      should allow.
  */
 public function __construct($level)
 {
     if (is_string($level)) {
         $plop = \Plop\Plop::getInstance();
         $level = \Plop\Plop::getLevelValue($level);
     }
     if (!is_int($level)) {
         throw new \Plop\Exception('Invalid value');
     }
     $this->level = $level;
 }
Esempio n. 9
0
 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;
 }
Esempio n. 10
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;
 }
Esempio n. 11
0
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $localChannel = $decoder->decodeUint32();
     $encoder = new \fpoirotte\Pssht\Wire\Encoder();
     $encoder->encodeUint32($localChannel);
     $decoder->getBuffer()->unget($encoder->getBuffer()->get(0));
     if (isset($this->handlers[$localChannel][$msgType])) {
         $handler = $this->handlers[$localChannel][$msgType];
         $logging = \Plop\Plop::getInstance();
         $logging->debug('Calling %(handler)s for channel #%(channel)d ' . 'with message type #%(msgType)d', array('handler' => get_class($handler) . '::handle', 'channel' => $localChannel, 'msgType' => $msgType));
         return $handler->handle($msgType, $decoder, $transport, $context);
     }
     return true;
 }
Esempio n. 12
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;
 }
Esempio n. 13
0
 public function handle($msgType, \fpoirotte\Pssht\Wire\Decoder $decoder, \fpoirotte\Pssht\Transport $transport, array &$context)
 {
     $hostAlgo = null;
     foreach ($context['kex']['client']->getServerHostKeyAlgos() as $algo) {
         if (isset($context['serverKeys'][$algo])) {
             $hostAlgo = $algo;
             break;
         }
     }
     if ($hostAlgo === null) {
         throw new \RuntimeException();
     }
     $response = $this->createResponse($decoder, $transport, $context, $hostAlgo);
     $logging = \Plop\Plop::getInstance();
     $secret = gmp_strval($response->getSharedSecret(), 16);
     $logging->debug("Shared secret:\r\n%s", array(wordwrap($secret, 16, ' ', true)));
     $logging->debug('Hash: %s', array(wordwrap(bin2hex($response->getExchangeHash()), 16, ' ', true)));
     if (!isset($context['sessionIdentifier'])) {
         $context['sessionIdentifier'] = $response->getExchangeHash();
     }
     $context['DH'] = $response;
     $transport->writeMessage($response);
     return true;
 }
Esempio n. 14
0
 /**
  * Run this CLI script.
  *
  * \param array $args
  *      A list of arguments passed to this script.
  *
  * \retval int
  *      Exit code. \c 0 is used to indicate a
  *      success, while any other code indicates
  *      an error.
  *
  * \note
  *      In case of an error, additional messages
  *      may be sent to \c STDERR by this script.
  */
 public function run(array $args)
 {
     $prog = array_shift($args);
     try {
         list($options, $params) = $this->parse($args);
     } catch (\Exception $e) {
         fprintf(STDERR, '%s: %s' . PHP_EOL, $prog, $e->getMessage());
         return 2;
     }
     // Show help.
     if ($options['h']) {
         $this->printUsage(new \fpoirotte\XRL\Output(STDOUT), $prog);
         return 0;
     }
     // Show version.
     if ($options['V']) {
         $version = self::getVersion();
         $license = self::getCopyrightAndLicense();
         echo 'XRL version ' . $version . PHP_EOL;
         echo PHP_EOL . $license . PHP_EOL;
         echo 'Visit https://github.com/fpoirotte/XRL for more!' . PHP_EOL;
         return 0;
     }
     // Do we have enough arguments to do something?
     if ($params['serverURL'] === null || $params['procedure'] === null) {
         $this->printUsage(new \fpoirotte\XRL\Output(STDERR), $prog);
         return 2;
     }
     // Then let's do it!
     $encoder = new \fpoirotte\XRL\NativeEncoder(new \fpoirotte\XRL\Encoder($options['t'], true));
     $decoder = new \fpoirotte\XRL\NativeDecoder(new \fpoirotte\XRL\Decoder($options['t'], $options['x']));
     $request = new \fpoirotte\XRL\Request($params['procedure'], $params['additional']);
     // Change verbosity as necessary.
     if (class_exists('\\Plop\\Plop')) {
         $logging = \Plop\Plop::getInstance();
         $logging->getLogger()->setLevel(40 - max(4, $options['v']) * 10);
     } else {
         $logging = null;
     }
     // Prepare the request.
     $xml = $encoder->encodeRequest($request);
     $logging and $logging->debug("Request:\n%(request)s", array('request' => $xml));
     if ($options['n']) {
         echo 'Not sending the actual query due to dry run mode.' . PHP_EOL;
         return 0;
     }
     // Prepare the context.
     $ctxOptions = array('http' => array('method' => 'POST', 'content' => $xml, 'header' => 'Content-Type: text/xml'));
     $context = stream_context_create($ctxOptions);
     libxml_set_streams_context($context);
     // Send the request and process the response.
     try {
         $result = $decoder->decodeResponse($params['serverURL']);
     } catch (\Exception $result) {
         // Nothing to do.
     }
     echo 'Result:' . PHP_EOL . print_r($result, true) . PHP_EOL;
     return 0;
 }
Esempio n. 15
0
<?php

require dirname(__DIR__) . DIRECTORY_SEPARATOR . 'vendor' . DIRECTORY_SEPARATOR . 'autoload.php';
$logging = \Plop\Plop::getInstance();
$handlers = $logging->getLogger()->getHandlers();
$handlers[0] = new \Plop\Handler\Stream(fopen('/dev/null', 'w'));
Esempio n. 16
0
 /**
  * Check for valid algorithm names.
  *
  *  \param string $class
  *      Name of the class whose algorithm name must be checked.
  *
  *  \retval string
  *      The class' algorithm name.
  *
  *  \retval null
  *      No valid algorithm name for the given class.
  */
 protected function getValidAlgorithm($class)
 {
     $logging = \Plop\Plop::getInstance();
     $w = 'abcdefghijklmnopqrstuvwxyz' . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . '1234567890-@.';
     $name = $class::getName();
     if (!is_string($name) || strspn($name, $w) !== strlen($name)) {
         $logging->debug('Skipping algorithm "%(name)s" (invalid algorithm name)', array('name' => $name));
         return null;
     }
     return $name;
 }
Esempio n. 17
0
 /**
  * Construct a new SSH_MSG_KEXDH_REPLY message.
  *
  *  \param fpoirotte::Pssht::ECC::Curve $curve
  *      Elliptic curve in use.
  *
  *  \param fpoirotte::Pssht::Messages::KEX::ECDH::INIT::RFC5656 $kexDHInit
  *      Client's contribution to the Diffie-Hellman Key Exchange.
  *
  *  \param fpoirotte::Pssht::PublicKeyInterface $key
  *      Server's public key.
  *
  *  \param fpoirotte::Pssht::EncryptionInterface $encryptionAlgo
  *      Encryption algorithm in use.
  *
  *  \param fpoirotte::Pssht::KEXInterface $kexAlgo
  *      Key exchange algorithm to use.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $serverKEX
  *      Algorithms supported by the server.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $clientKEX
  *      Algorithms supported by the client.
  *
  *  \param string $serverIdent
  *      Server's identification string
  *
  *  \param string $clientIdent
  *      Client's identification string
  */
 public function __construct(\fpoirotte\Pssht\ECC\Curve $curve, \fpoirotte\Pssht\Messages\KEX\ECDH\INIT\RFC5656 $kexDHInit, \fpoirotte\Pssht\PublicKeyInterface $key, \fpoirotte\Pssht\EncryptionInterface $encryptionAlgo, \fpoirotte\Pssht\KEXInterface $kexAlgo, \fpoirotte\Pssht\Messages\KEXINIT $serverKEX, \fpoirotte\Pssht\Messages\KEXINIT $clientKEX, $serverIdent, $clientIdent)
 {
     if (!is_string($serverIdent)) {
         throw new \InvalidArgumentException();
     }
     if (!is_string($clientIdent)) {
         throw new \InvalidArgumentException();
     }
     $len = strlen(gmp_strval($curve->getOrder(), 2));
     $len = ceil($len / 8);
     $randBytes = openssl_random_pseudo_bytes($len);
     $d_S = gmp_mod(gmp_init(bin2hex($randBytes), 16), $curve->getModulus());
     $this->Q_S = $curve->getGenerator()->multiply($curve, $d_S);
     $Q_C = $kexDHInit->getQ();
     /// @FIXME this is not optimal...
     $algorithms = \fpoirotte\Pssht\Algorithms::factory();
     $cls = $algorithms->getClass('PublicKey', 'ecdsa-sha2-' . $curve->getName());
     $clientPK = new $cls($Q_C);
     if (!$clientPK->isValid()) {
         throw new \InvalidArgumentException();
     }
     // EC Co-factor DH (sec1-v2, section 3.3.2).
     $P = $Q_C->multiply($curve, gmp_mul($curve->getCofactor(), $d_S));
     if ($P->isIdentity($curve)) {
         throw new \InvalidArgumentException();
     }
     $this->K = $P->x;
     $this->curve = $curve;
     $this->K_S = $key;
     $this->kexDHInit = $kexDHInit;
     $this->kexAlgo = $kexAlgo;
     $this->serverKEX = $serverKEX;
     $this->clientKEX = $clientKEX;
     $this->serverIdent = $serverIdent;
     $this->clientIdent = $clientIdent;
     $msgId = chr(\fpoirotte\Pssht\Messages\KEXINIT::getMessageId());
     // $sub is used to create the structure for the hashing function.
     $sub = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $this->K_S->serialize($sub);
     $K_S = $sub->getBuffer()->get(0);
     $sub->encodeString($this->clientIdent);
     $sub->encodeString($this->serverIdent);
     // $sub2 is used to compute the value
     // of various fields inside the structure.
     $sub2 = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->clientKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->serverKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub->encodeString($K_S);
     $sub->encodeString($Q_C->serialize($curve));
     $sub->encodeString($this->Q_S->serialize($curve));
     $sub->encodeMpint($this->K);
     $logging = \Plop\Plop::getInstance();
     $origData = $sub->getBuffer()->get(0);
     $data = wordwrap(bin2hex($origData), 4, ' ', true);
     $data = wordwrap($data, 32 + 7, PHP_EOL, true);
     $logging->debug("Signature payload:\r\n%s", array($data));
     $this->H = $this->kexAlgo->hash($origData);
 }
Esempio n. 18
0
 /**
  * Construct a new SSH_MSG_KEXDH_REPLY message.
  *
  *  \param fpoirotte::Pssht::Messages::KEXDH::INIT $kexDHInit
  *      Client's contribution to the Diffie-Hellman Key Exchange.
  *
  *  \param fpoirotte::Pssht::PublicKeyInterface $key
  *      Server's public key.
  *
  *  \param fpoirotte::Pssht::EncryptionInterface $encryptionAlgo
  *      Encryption algorithm in use.
  *
  *  \param fpoirotte::Pssht::KEXInterface $kexAlgo
  *      Key exchange algorithm to use.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $serverKEX
  *      Algorithms supported by the server.
  *
  *  \param fpoirotte::Pssht::Messages::KEXINIT $clientKEX
  *      Algorithms supported by the client.
  *
  *  \param string $serverIdent
  *      Server's identification string
  *
  *  \param string $clientIdent
  *      Client's identification string
  */
 public function __construct(\fpoirotte\Pssht\Messages\KEXDH\INIT $kexDHInit, \fpoirotte\Pssht\PublicKeyInterface $key, \fpoirotte\Pssht\EncryptionInterface $encryptionAlgo, \fpoirotte\Pssht\KEXInterface $kexAlgo, \fpoirotte\Pssht\Messages\KEXINIT $serverKEX, \fpoirotte\Pssht\Messages\KEXINIT $clientKEX, $serverIdent, $clientIdent)
 {
     if (!is_string($serverIdent)) {
         throw new \InvalidArgumentException();
     }
     if (!is_string($clientIdent)) {
         throw new \InvalidArgumentException();
     }
     $keyLength = min(20, max($encryptionAlgo->getKeySize(), 16));
     $randBytes = openssl_random_pseudo_bytes(2 * $keyLength);
     $y = gmp_init(bin2hex($randBytes), 16);
     $prime = gmp_init($kexAlgo::getPrime(), 16);
     $this->f = gmp_powm($kexAlgo::getGenerator(), $y, $prime);
     $this->K = gmp_powm($kexDHInit->getE(), $y, $prime);
     $this->K_S = $key;
     $this->kexDHInit = $kexDHInit;
     $this->kexAlgo = $kexAlgo;
     $this->serverKEX = $serverKEX;
     $this->clientKEX = $clientKEX;
     $this->serverIdent = $serverIdent;
     $this->clientIdent = $clientIdent;
     $msgId = chr(\fpoirotte\Pssht\Messages\KEXINIT::getMessageId());
     // $sub is used to create the structure for the hashing function.
     $sub = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $this->K_S->serialize($sub);
     $K_S = $sub->getBuffer()->get(0);
     $sub->encodeString($this->clientIdent);
     $sub->encodeString($this->serverIdent);
     // $sub2 is used to compute the value
     // of various fields inside the structure.
     $sub2 = new \fpoirotte\Pssht\Wire\Encoder(new \fpoirotte\Pssht\Buffer());
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->clientKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub2->encodeBytes($msgId);
     // Add message identifier.
     $this->serverKEX->serialize($sub2);
     $sub->encodeString($sub2->getBuffer()->get(0));
     $sub->encodeString($K_S);
     $sub->encodeMpint($this->kexDHInit->getE());
     $sub->encodeMpint($this->f);
     $sub->encodeMpint($this->K);
     $logging = \Plop\Plop::getInstance();
     $origData = $sub->getBuffer()->get(0);
     $data = wordwrap(bin2hex($origData), 4, ' ', true);
     $data = wordwrap($data, 32 + 7, PHP_EOL, true);
     $logging->debug("Signature payload:\r\n%s", array($data));
     $this->H = $this->kexAlgo->hash($origData);
 }
Esempio n. 19
0
File: Base.php Progetto: erebot/api
 /**
  * Constructor for modules.
  *
  * \param string|null $channel
  *      (optional) The channel this instance applies to.
  *      This will be \b null for modules loaded at the server
  *      level or higher in the configuration hierarchy.
  */
 public final function __construct($channel)
 {
     $this->connection = $this->translator = $this->mainCfg = null;
     $this->channel = $channel;
     $this->factories = array();
     $ifaces = array('!EventHandler' => '\\Erebot\\EventHandler', '!Identity' => '\\Erebot\\Identity', '!NumericHandler' => '\\Erebot\\NumericHandler', '!NumericReference' => '\\Erebot\\NumericReference', '!Styling' => '\\Erebot\\Styling', '\\Erebot\\Styling\\Variables\\CurrencyInterface' => '\\Erebot\\Styling\\Variables\\CurrencyVariable', '\\Erebot\\Styling\\Variables\\DateTimeInterface' => '\\Erebot\\Styling\\Variables\\DateTimeVariable', '\\Erebot\\Styling\\Variables\\DurationInterface' => '\\Erebot\\Styling\\Variables\\DurationVariable', '!TextWrapper' => '\\Erebot\\TextWrapper', '!Timer' => '\\Erebot\\Timer');
     foreach ($ifaces as $iface => $cls) {
         try {
             $this->setFactory($iface, $cls);
         } catch (\Erebot\InvalidValueException $e) {
             // Ignore silently as the only time the default classes
             // won't exist is when we run the tests for some module.
         }
     }
     /// @FIXME: handle dependency injection somehow
     $this->logger = null;
     if (class_exists('\\Plop\\Plop')) {
         $this->logger =& \Plop\Plop::getInstance();
     }
 }
Esempio n. 20
0
 public function load($configData, $source)
 {
     $logger = \Plop\Plop::getInstance();
     $possibleSources = array(self::LOAD_FROM_FILE, self::LOAD_FROM_STRING);
     if (!in_array($source, $possibleSources, true)) {
         throw new \Erebot\InvalidValueException('Invalid $source');
     }
     if ($source == self::LOAD_FROM_FILE) {
         if (is_string($configData) && $configData != "") {
             if (!strncasecmp(PHP_OS, "Win", 3)) {
                 if (!in_array($configData[0], array("/", "\\")) && strlen($configData) > 1 && $configData[1] != ":") {
                     $configData = getcwd() . DIRECTORY_SEPARATOR . $configData;
                 }
             } elseif ($configData[0] != DIRECTORY_SEPARATOR) {
                 $configData = getcwd() . DIRECTORY_SEPARATOR . $configData;
             }
             $file = \Erebot\URI::fromAbsPath($configData, false);
         } elseif (is_object($configData) && $configData instanceof \Erebot\URIInterface) {
             $file = $configData;
         } else {
             throw new \Erebot\InvalidValueException("Invalid configuration file");
         }
     } elseif (!is_string($configData)) {
         throw new \Erebot\InvalidValueException("Invalid configuration file");
     } else {
         $file = null;
     }
     $mainSchema = dirname(dirname(__DIR__)) . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'config.rng';
     $mainSchema = file_get_contents($mainSchema);
     $ue = libxml_use_internal_errors(true);
     $domxml = new \Erebot\DOM();
     if ($source == self::LOAD_FROM_FILE) {
         $domxml->load((string) $file);
     } else {
         $domxml->loadXML($configData);
     }
     $domxml->xinclude(LIBXML_NOBASEFIX);
     $this->stripXGlobWrappers($domxml);
     $ok = $domxml->relaxNGValidateSource($mainSchema);
     $errors = $domxml->getErrors();
     libxml_use_internal_errors($ue);
     if (!$ok || count($errors)) {
         // Some unpredicted error occurred,
         // show some (hopefully) useful information.
         $logger->error(print_r($errors, true));
         throw new \Erebot\InvalidValueException('Errors were found while validating the configuration file');
     }
     $xml = simplexml_import_dom($domxml);
     parent::__construct($this, $xml);
     if (!isset($xml['version'])) {
         $this->version = null;
     } else {
         $this->version = (string) $xml['version'];
     }
     if (!isset($xml['timezone'])) {
         throw new \Erebot\InvalidValueException('No timezone defined');
     }
     $this->timezone = (string) $xml['timezone'];
     // Set timezone information.
     // This is needed to configure the logging subsystem.
     if (function_exists('date_default_timezone_set')) {
         if (!date_default_timezone_set($this->timezone)) {
             throw \Erebot\InvalidValueException(sprintf('Invalid timezone: "%s"', $this->timezone));
         }
     }
     $daemonize = isset($xml['daemon']) ? $this->parseBool((string) $xml['daemon']) : false;
     $userIdentity = isset($xml['uid']) ? (string) $xml['uid'] : null;
     $groupIdentity = isset($xml['gid']) ? (string) $xml['gid'] : null;
     $pidfile = isset($xml['pidfile']) ? (string) $xml['pidfile'] : null;
     if ($daemonize === null) {
         throw new \Erebot\InvalidValueException('Invalid "daemon" value');
     }
     if (!isset($xml['commands-prefix'])) {
         $this->commandsPrefix = '!';
     } else {
         $this->commandsPrefix = (string) $xml['commands-prefix'];
         if (strcspn($this->commandsPrefix, " \r\n\t") != strlen($this->commandsPrefix)) {
             throw new \Erebot\InvalidValueException('Invalid command prefix');
         }
     }
     $logger->debug($this->coreTranslator->gettext('Loaded configuration data'));
     $this->networks = array();
     foreach ($xml->networks->network as $netCfg) {
         /// @TODO use dependency injection instead.
         $newConfig = new \Erebot\Config\Network($this, $netCfg);
         $this->networks[$newConfig->getName()] = $newConfig;
         unset($newConfig);
     }
     if ($source == self::LOAD_FROM_FILE) {
         $this->configFile = $configData;
     } else {
         $this->configFile = null;
     }
     // Default values.
     $this->daemonize = $daemonize;
     $this->userIdentity = $userIdentity;
     $this->groupIdentity = $groupIdentity;
     $this->pidfile = $pidfile;
 }
Esempio n. 21
0
 /**
  * Try to read and handle a single SSH message.
  *
  *  \retval bool
  *      \b true if a message was successfully read and handled,
  *      \b false otherwise.
  *
  *  \note
  *      Depending on the circumstances, messages may be successfully
  *      read but left unhandled (eg. because the message was incomplete).
  *      In such cases, the message will be reinjected and \b false
  *      returned, making it possible for a future call to this method
  *      to handle the (full) message again.
  */
 public function readMessage()
 {
     $logging = \Plop\Plop::getInstance();
     // Initial state: expect the client's identification string.
     if (!isset($this->context['identity']['client'])) {
         return $this->handlers[256]->handle(null, $this->decoder, $this, $this->context);
     }
     $blockSize = max($this->decryptor->getBlockSize(), 8);
     // See http://api.libssh.org/rfc/PROTOCOL
     // for more information on EtM (Encrypt-then-MAC).
     if ($this->inMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) {
         $encPayload = $this->decoder->getBuffer()->get(4);
         if ($encPayload === null) {
             return false;
         }
         $unencrypted = $encPayload;
     } elseif ($this->decryptor instanceof \fpoirotte\Pssht\AEADInterface) {
         $encPayload = $this->decoder->getBuffer()->get(4);
         if ($encPayload === null) {
             return false;
         }
         $unencrypted = $this->decryptor->decrypt($this->inSeqNo, $encPayload);
         $this->decoder->getBuffer()->unget($encPayload);
         $encPayload = '';
     } else {
         $encPayload = $this->decoder->getBuffer()->get($blockSize);
         if ($encPayload === null) {
             return false;
         }
         $unencrypted = $this->decryptor->decrypt($this->inSeqNo, $encPayload);
     }
     $buffer = new \fpoirotte\Pssht\Buffer($unencrypted);
     $decoder = new \fpoirotte\Pssht\Wire\Decoder($buffer);
     $packetLength = $decoder->decodeUint32();
     // Read the rest of the message.
     if ($this->inMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) {
         // Only the main payload remains.
         $toRead = $packetLength;
     } elseif ($this->decryptor instanceof \fpoirotte\Pssht\AEADInterface) {
         // packet length (authenticated data)
         // + encrypted payload
         // + authentication tag (AT)
         $toRead = 4 + $packetLength + $this->decryptor->getSize();
     } else {
         $toRead = 4 - $blockSize + $packetLength;
     }
     if ($toRead < 0) {
         throw new \RuntimeException();
     }
     $unencrypted2 = '';
     if ($toRead !== 0) {
         $encPayload2 = $this->decoder->getBuffer()->get($toRead);
         if ($encPayload2 === null) {
             $this->decoder->getBuffer()->unget($encPayload);
             return false;
         }
         $unencrypted2 = $this->decryptor->decrypt($this->inSeqNo, $encPayload2);
         if ($unencrypted2 === null) {
             return false;
         }
         $buffer->push($unencrypted2);
     }
     $paddingLength = ord($decoder->decodeBytes());
     $payload = $decoder->decodeBytes($packetLength - $paddingLength - 1);
     $padding = $decoder->decodeBytes($paddingLength);
     // If a MAC is in use.
     $macSize = $this->inMAC->getSize();
     $actualMAC = '';
     if ($macSize > 0) {
         $actualMAC = $this->decoder->getBuffer()->get($macSize);
         if ($actualMAC === null) {
             $this->decoder->getBuffer()->unget($encPayload2)->unget($encPayload);
             return false;
         }
         if ($this->inMAC instanceof \fpoirotte\Pssht\MAC\OpensshCom\EtM\EtMInterface) {
             // $encPayload actually contains packet length (in plaintext).
             $macData = $encPayload . $encPayload2;
         } else {
             $macData = $unencrypted . $unencrypted2;
         }
         $expectedMAC = $this->inMAC->compute($this->inSeqNo, (string) substr($macData, 0, $packetLength + 4));
         if ($expectedMAC !== $actualMAC) {
             throw new \RuntimeException();
         }
     }
     if (!isset($packetLength, $paddingLength, $payload, $padding, $actualMAC)) {
         $this->decoder->getBuffer()->unget($actualMAC)->unget($encPayload2)->unget($encPayload);
         $logging->error('Something went wrong during decoding');
         return false;
     }
     $payload = $this->uncompressor->update($payload);
     $decoder = new \fpoirotte\Pssht\Wire\Decoder(new \fpoirotte\Pssht\Buffer($payload));
     $msgType = ord($decoder->decodeBytes(1));
     $logging->debug('Received payload: %s', array(\escape($payload)));
     $res = true;
     if (isset($this->handlers[$msgType])) {
         $handler = $this->handlers[$msgType];
         $logging->debug('Calling %(handler)s with message type #%(msgType)d', array('handler' => get_class($handler) . '::handle', 'msgType' => $msgType));
         try {
             $res = $handler->handle($msgType, $decoder, $this, $this->context);
         } catch (\fpoirotte\Pssht\Messages\DISCONNECT $e) {
             if ($e->getCode() !== 0) {
                 $this->writeMessage($e);
             }
             throw $e;
         }
     } else {
         $logging->warn('Unimplemented message type (%d)', array($msgType));
         $response = new \fpoirotte\Pssht\Messages\UNIMPLEMENTED($this->inSeqNo);
         $this->writeMessage($response);
     }
     $this->inSeqNo = ++$this->inSeqNo & 0xffffffff;
     return $res;
 }
Esempio n. 22
0
 /**
  * Creates the connections for this instance.
  *
  * \param Erebot::Interfaces::ConnectionFactory $factory
  *      The factory to use to create the connections.
  *
  * \param Erebot::Interfaces::Config::Main $config
  *      The main configuration for the bot.
  */
 protected function createConnections(\Erebot\Interfaces\ConnectionFactory $factory, \Erebot\Interfaces\Config\Main $config)
 {
     $logger = \Plop\Plop::getInstance();
     // List existing connections so they
     // can eventually be reused.
     $newConnections = $currentConnections = array();
     foreach ($this->connections as $connection) {
         $connCfg = $connection->getConfig(null);
         if ($connCfg) {
             $netCfg = $connCfg->getNetworkCfg();
             $currentConnections[$netCfg->getName()] = $connection;
         } else {
             $newConnections[] = $connection;
         }
     }
     // Let's establish some contacts.
     $networks = $config->getNetworks();
     foreach ($networks as $network) {
         $netName = $network->getName();
         if (isset($currentConnections[$netName])) {
             try {
                 $uris = $currentConnections[$netName]->getConfig(null)->getConnectionURI();
                 $uri = new \Erebot\URI($uris[count($uris) - 1]);
                 $serverCfg = $network->getServerCfg((string) $uri);
                 $logger->info($this->gettext('Reusing existing connection ' . 'for network "%(network)s"'), array('network' => $netName));
                 // Move it from existing connections to new connections,
                 // marking it as still being in use.
                 $copy = clone $currentConnections[$netName];
                 $copy->reload($serverCfg);
                 $newConnections[] = $copy;
                 unset($currentConnections[$netName]);
                 continue;
             } catch (\Erebot\NotFoundException $e) {
                 // Nothing to do.
             }
         }
         if (!in_array('\\Erebot\\Module\\AutoConnect', $network->getModules(true))) {
             continue;
         }
         $servers = $network->getServers();
         foreach ($servers as $server) {
             $uris = $server->getConnectionURI();
             $serverUri = new \Erebot\URI($uris[count($uris) - 1]);
             try {
                 $connection = $factory->newConnection($this, $server);
                 // Drop connection to a (now-)unconfigured
                 // server on that network.
                 if (isset($currentConnections[$netName])) {
                     $currentConnections[$netName]->disconnect();
                     unset($currentConnections[$netName]);
                 }
                 $logger->info($this->gettext('Trying to connect to "%(uri)s"...'), array('uri' => $serverUri));
                 $connection->connect();
                 $newConnections[] = $connection;
                 $logger->info($this->gettext('Successfully connected to "%(uri)s"...'), array('uri' => $serverUri));
                 break;
             } catch (\Erebot\ConnectionFailureException $e) {
                 // Nothing to do... We simply
                 // try the next server on the
                 // list until we successfully
                 // connect or cycle the list.
                 $logger->exception($this->gettext('Could not connect to "%(uri)s"'), $e, array('uri' => $serverUri));
             }
         }
     }
     // Gracefully quit leftover connections.
     foreach ($currentConnections as $connection) {
         $connection->disconnect();
     }
     $this->connections = $newConnections;
     $this->mainCfg = $config;
 }
Esempio n. 23
0
 public function isEnabledFor($level)
 {
     if (!is_int($level)) {
         $plop = \Plop\Plop::getInstance();
         $level = $plop->getLevelValue($level);
     }
     return $level >= $this->level;
 }
Esempio n. 24
0
 /**
  * Constructs the UNIX socket that represents the prompt.
  *
  * \param Erebot::Interfaces::Core $bot
  *      Instance of the bot to operate on.
  *
  * \param string $connector
  *      (optional) Path where the newly-created UNIX socket
  *      will be made accessible. The default is to create
  *      a UNIX socket named "Erebot.sock" in the system's
  *      temporary directory (usually "/tmp/").
  *
  * \param mixed $group
  *      (optional) Either the name or the identifier
  *      of the UNIX group the socket will belong to.
  *      The default is to not change the group of the
  *      socket (i.e. to keep whatever is the main group
  *      for the user running Erebot).
  *
  * \param int $perms
  *      (optional) UNIX permissions the newly-created
  *      socket will receive. The default is to give
  *      read/write access to the user running Erebot and
  *      to the group the socket belongs to (see $group).
  *
  * \note
  *      On most systems, only the superuser may change
  *      the group of a file arbitrarily, while other
  *      users may only change it to a group they are
  *      a member of.
  *
  * \warning
  *      Using the wrong combination of $group and $perms
  *      may lead to security issues. Use with caution.
  *      The default values are safe as long as you trust
  *      users belonging to the same group as the main
  *      group of the user running Erebot.
  *
  * \see
  *      http://php.net/chmod provides more information
  *      on the meaning of $perms' value
  *      (see the description for \c mode).
  */
 public function __construct(\Erebot\Interfaces\Core $bot, $connector = null, $group = null, $perms = 0660)
 {
     $this->bot = $bot;
     if ($connector === null) {
         $connector = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'Erebot.sock';
     }
     $this->socket = stream_socket_server("udg://" . $connector, $errno, $errstr, STREAM_SERVER_BIND);
     if (!$this->socket) {
         throw new \Exception("Could not create prompt (" . $errstr . ")");
     }
     // Cleanup on shutdown.
     register_shutdown_function(function ($socket) {
         @unlink($socket);
     }, $connector);
     // Change group.
     if ($group !== null) {
         if (!@chgrp($connector, $group)) {
             throw new \Exception("Could not change group to '{$group}' for '{$connector}'");
         }
     }
     // Change permissions.
     if (!chmod($connector, $perms)) {
         throw new \Exception("Could not set permissions to {$perms} on '{$connector}'");
     }
     // Flush any received data on the socket, because
     // any data sent before the group and permissions
     // were set may have come from an untrusted source.
     $flush = array($this->socket);
     $dummy = null;
     while (stream_select($flush, $dummy, $dummy, 0) == 1) {
         if (fread($this->socket, 8192) === false) {
             throw new \Exception("Error while flushing the socket");
         }
     }
     $this->io = new \Erebot\LineIO(\Erebot\LineIO::EOL_ANY, $this->socket);
     $logger = \Plop\Plop::getInstance();
     $logger->info($bot->gettext('Prompt started in "%(path)s"'), array('path' => $connector));
 }
Esempio n. 25
0
 /**
  * Writes a single line from the output buffer
  * to the socket.
  *
  * \retval int
  *      The number of bytes successfully
  *      written on the socket.
  *
  * \retval false
  *      The connection was lost while trying
  *      to send the line or the output buffer
  *      was empty.
  */
 public function write()
 {
     if (!count($this->sndQueue)) {
         return false;
     }
     $line = array_shift($this->sndQueue);
     $logger = \Plop\Plop::getInstance();
     // Make sure we send the whole line,
     // with a trailing CR LF sequence.
     $eol = $this->eol[count($this->eol) - 1];
     $line .= $eol;
     $len = strlen($line);
     for ($written = 0; $written < $len; $written += $fwrite) {
         $fwrite = @fwrite($this->socket, substr($line, $written));
         if ($fwrite === false) {
             return false;
         }
     }
     $line = substr($line, 0, -strlen($eol));
     $logger->debug('%(line)s', array('line' => addcslashes($line, "..")));
     return $written;
 }
Esempio n. 26
0
 /**
  * @covers \Plop\Plop::findCaller
  */
 public function testFindCallerMethod()
 {
     $line = __LINE__ + 1;
     $caller = \Plop\Plop::findCaller();
     // Strip the namespace from the class name.
     $cls = substr('\\' . __CLASS__, strrpos('\\' . __CLASS__, '\\') + 1);
     $this->assertSame(__NAMESPACE__, $caller['ns']);
     $this->assertSame(__FILE__, $caller['file']);
     $this->assertSame($line, $caller['line']);
     $this->assertSame(__FUNCTION__, $caller['func']);
     $this->assertSame($cls, $caller['cls']);
 }