316 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
		
		
			
		
	
	
			316 lines
		
	
	
		
			9.6 KiB
		
	
	
	
		
			PHP
		
	
	
	
|  | <?php | ||
|  | /** | ||
|  |  * Pure-PHP ssh-agent client. | ||
|  |  * | ||
|  |  * PHP versions 4 and 5 | ||
|  |  * | ||
|  |  * Here are some examples of how to use this library: | ||
|  |  * <code> | ||
|  |  * <?php | ||
|  |  *    include('System/SSH_Agent.php'); | ||
|  |  *    include('Net/SSH2.php'); | ||
|  |  * | ||
|  |  *    $agent = new System_SSH_Agent(); | ||
|  |  * | ||
|  |  *    $ssh = new Net_SSH2('www.domain.tld'); | ||
|  |  *    if (!$ssh->login('username', $agent)) { | ||
|  |  *        exit('Login Failed'); | ||
|  |  *    } | ||
|  |  * | ||
|  |  *    echo $ssh->exec('pwd'); | ||
|  |  *    echo $ssh->exec('ls -la'); | ||
|  |  * ?>
 | ||
|  |  * </code> | ||
|  |  * | ||
|  |  * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy | ||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||
|  |  * in the Software without restriction, including without limitation the rights | ||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||
|  |  * furnished to do so, subject to the following conditions: | ||
|  |  * | ||
|  |  * The above copyright notice and this permission notice shall be included in | ||
|  |  * all copies or substantial portions of the Software. | ||
|  |  * | ||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
|  |  * THE SOFTWARE. | ||
|  |  * | ||
|  |  * @category  System | ||
|  |  * @package   System_SSH_Agent | ||
|  |  * @author    Jim Wigginton <terrafrost@php.net> | ||
|  |  * @copyright MMXIV Jim Wigginton | ||
|  |  * @license   http://www.opensource.org/licenses/mit-license.html  MIT License | ||
|  |  * @link      http://phpseclib.sourceforge.net | ||
|  |  * @internal  See http://api.libssh.org/rfc/PROTOCOL.agent | ||
|  |  */ | ||
|  | 
 | ||
|  | /**#@+
 | ||
|  |  * Message numbers | ||
|  |  * | ||
|  |  * @access private | ||
|  |  */ | ||
|  | // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
 | ||
|  | define('SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES', 11); | ||
|  | // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
 | ||
|  | define('SYSTEM_SSH_AGENT_IDENTITIES_ANSWER', 12); | ||
|  | define('SYSTEM_SSH_AGENT_FAILURE', 5); | ||
|  | // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3)
 | ||
|  | define('SYSTEM_SSH_AGENTC_SIGN_REQUEST', 13); | ||
|  | // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
 | ||
|  | define('SYSTEM_SSH_AGENT_SIGN_RESPONSE', 14); | ||
|  | /**#@-*/ | ||
|  | 
 | ||
|  | /** | ||
|  |  * Pure-PHP ssh-agent client identity object | ||
|  |  * | ||
|  |  * Instantiation should only be performed by System_SSH_Agent class. | ||
|  |  * This could be thought of as implementing an interface that Crypt_RSA | ||
|  |  * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something. | ||
|  |  * The methods in this interface would be getPublicKey, setSignatureMode | ||
|  |  * and sign since those are the methods phpseclib looks for to perform | ||
|  |  * public key authentication. | ||
|  |  * | ||
|  |  * @package System_SSH_Agent | ||
|  |  * @author  Jim Wigginton <terrafrost@php.net> | ||
|  |  * @version 0.1.0 | ||
|  |  * @access  internal | ||
|  |  */ | ||
|  | class System_SSH_Agent_Identity | ||
|  | { | ||
|  |     /** | ||
|  |      * Key Object | ||
|  |      * | ||
|  |      * @var Crypt_RSA | ||
|  |      * @access private | ||
|  |      * @see System_SSH_Agent_Identity::getPublicKey() | ||
|  |      */ | ||
|  |     var $key; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Key Blob | ||
|  |      * | ||
|  |      * @var String | ||
|  |      * @access private | ||
|  |      * @see System_SSH_Agent_Identity::sign() | ||
|  |      */ | ||
|  |     var $key_blob; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Socket Resource | ||
|  |      * | ||
|  |      * @var Resource | ||
|  |      * @access private | ||
|  |      * @see System_SSH_Agent_Identity::sign() | ||
|  |      */ | ||
|  |     var $fsock; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Default Constructor. | ||
|  |      * | ||
|  |      * @param Resource $fsock | ||
|  |      * @return System_SSH_Agent_Identity | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function System_SSH_Agent_Identity($fsock) | ||
|  |     { | ||
|  |         $this->fsock = $fsock; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Set Public Key | ||
|  |      * | ||
|  |      * Called by System_SSH_Agent::requestIdentities() | ||
|  |      * | ||
|  |      * @param Crypt_RSA $key | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function setPublicKey($key) | ||
|  |     { | ||
|  |         $this->key = $key; | ||
|  |         $this->key->setPublicKey(); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Set Public Key | ||
|  |      * | ||
|  |      * Called by System_SSH_Agent::requestIdentities(). The key blob could be extracted from $this->key | ||
|  |      * but this saves a small amount of computation. | ||
|  |      * | ||
|  |      * @param String $key_blob | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     function setPublicKeyBlob($key_blob) | ||
|  |     { | ||
|  |         $this->key_blob = $key_blob; | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Get Public Key | ||
|  |      * | ||
|  |      * Wrapper for $this->key->getPublicKey() | ||
|  |      * | ||
|  |      * @param Integer $format optional | ||
|  |      * @return Mixed | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function getPublicKey($format = null) | ||
|  |     { | ||
|  |         return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format); | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Set Signature Mode | ||
|  |      * | ||
|  |      * Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie. | ||
|  |      * ssh-agent's only supported mode is CRYPT_RSA_SIGNATURE_PKCS1 | ||
|  |      * | ||
|  |      * @param Integer $mode | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function setSignatureMode($mode) | ||
|  |     { | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Create a signature | ||
|  |      * | ||
|  |      * See "2.6.2 Protocol 2 private key signature request" | ||
|  |      * | ||
|  |      * @param String $message | ||
|  |      * @return String | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function sign($message) | ||
|  |     { | ||
|  |         // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
 | ||
|  |         $packet = pack('CNa*Na*N', SYSTEM_SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0); | ||
|  |         $packet = pack('Na*', strlen($packet), $packet); | ||
|  |         if (strlen($packet) != fputs($this->fsock, $packet)) { | ||
|  |             user_error('Connection closed during signing'); | ||
|  |         } | ||
|  | 
 | ||
|  |         $length = current(unpack('N', fread($this->fsock, 4))); | ||
|  |         $type = ord(fread($this->fsock, 1)); | ||
|  |         if ($type != SYSTEM_SSH_AGENT_SIGN_RESPONSE) { | ||
|  |             user_error('Unable to retreive signature'); | ||
|  |         } | ||
|  | 
 | ||
|  |         $signature_blob = fread($this->fsock, $length - 1); | ||
|  |         // the only other signature format defined - ssh-dss - is the same length as ssh-rsa
 | ||
|  |         // the + 12 is for the other various SSH added length fields
 | ||
|  |         return substr($signature_blob, strlen('ssh-rsa') + 12); | ||
|  |     } | ||
|  | } | ||
|  | 
 | ||
|  | /** | ||
|  |  * Pure-PHP ssh-agent client identity factory | ||
|  |  * | ||
|  |  * requestIdentities() method pumps out System_SSH_Agent_Identity objects | ||
|  |  * | ||
|  |  * @package System_SSH_Agent | ||
|  |  * @author  Jim Wigginton <terrafrost@php.net> | ||
|  |  * @version 0.1.0 | ||
|  |  * @access  internal | ||
|  |  */ | ||
|  | class System_SSH_Agent | ||
|  | { | ||
|  |     /** | ||
|  |      * Socket Resource | ||
|  |      * | ||
|  |      * @var Resource | ||
|  |      * @access private | ||
|  |      */ | ||
|  |     var $fsock; | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Default Constructor | ||
|  |      * | ||
|  |      * @return System_SSH_Agent | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function System_SSH_Agent() | ||
|  |     { | ||
|  |         switch (true) { | ||
|  |             case isset($_SERVER['SSH_AUTH_SOCK']): | ||
|  |                 $address = $_SERVER['SSH_AUTH_SOCK']; | ||
|  |                 break; | ||
|  |             case isset($_ENV['SSH_AUTH_SOCK']): | ||
|  |                 $address = $_ENV['SSH_AUTH_SOCK']; | ||
|  |                 break; | ||
|  |             default: | ||
|  |                 user_error('SSH_AUTH_SOCK not found'); | ||
|  |                 return false; | ||
|  |         } | ||
|  | 
 | ||
|  |         $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); | ||
|  |         if (!$this->fsock) { | ||
|  |             user_error("Unable to connect to ssh-agent (Error $errno: $errstr)"); | ||
|  |         } | ||
|  |     } | ||
|  | 
 | ||
|  |     /** | ||
|  |      * Request Identities | ||
|  |      * | ||
|  |      * See "2.5.2 Requesting a list of protocol 2 keys" | ||
|  |      * Returns an array containing zero or more System_SSH_Agent_Identity objects | ||
|  |      * | ||
|  |      * @return Array | ||
|  |      * @access public | ||
|  |      */ | ||
|  |     function requestIdentities() | ||
|  |     { | ||
|  |         if (!$this->fsock) { | ||
|  |             return array(); | ||
|  |         } | ||
|  | 
 | ||
|  |         $packet = pack('NC', 1, SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES); | ||
|  |         if (strlen($packet) != fputs($this->fsock, $packet)) { | ||
|  |             user_error('Connection closed while requesting identities'); | ||
|  |         } | ||
|  | 
 | ||
|  |         $length = current(unpack('N', fread($this->fsock, 4))); | ||
|  |         $type = ord(fread($this->fsock, 1)); | ||
|  |         if ($type != SYSTEM_SSH_AGENT_IDENTITIES_ANSWER) { | ||
|  |             user_error('Unable to request identities'); | ||
|  |         } | ||
|  | 
 | ||
|  |         $identities = array(); | ||
|  |         $keyCount = current(unpack('N', fread($this->fsock, 4))); | ||
|  |         for ($i = 0; $i < $keyCount; $i++) { | ||
|  |             $length = current(unpack('N', fread($this->fsock, 4))); | ||
|  |             $key_blob = fread($this->fsock, $length); | ||
|  |             $length = current(unpack('N', fread($this->fsock, 4))); | ||
|  |             $key_comment = fread($this->fsock, $length); | ||
|  |             $length = current(unpack('N', substr($key_blob, 0, 4))); | ||
|  |             $key_type = substr($key_blob, 4, $length); | ||
|  |             switch ($key_type) { | ||
|  |                 case 'ssh-rsa': | ||
|  |                     if (!class_exists('Crypt_RSA')) { | ||
|  |                         include_once 'Crypt/RSA.php'; | ||
|  |                     } | ||
|  |                     $key = new Crypt_RSA(); | ||
|  |                     $key->loadKey('ssh-rsa ' . base64_encode($key_blob) . ' ' . $key_comment); | ||
|  |                     break; | ||
|  |                 case 'ssh-dss': | ||
|  |                     // not currently supported
 | ||
|  |                     break; | ||
|  |             } | ||
|  |             // resources are passed by reference by default
 | ||
|  |             if (isset($key)) { | ||
|  |                 $identity = new System_SSH_Agent_Identity($this->fsock); | ||
|  |                 $identity->setPublicKey($key); | ||
|  |                 $identity->setPublicKeyBlob($key_blob); | ||
|  |                 $identities[] = $identity; | ||
|  |                 unset($key); | ||
|  |             } | ||
|  |         } | ||
|  | 
 | ||
|  |         return $identities; | ||
|  |     } | ||
|  | } |