Agent.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  1. <?php
  2. /**
  3. * Pure-PHP ssh-agent client.
  4. *
  5. * PHP versions 4 and 5
  6. *
  7. * Here are some examples of how to use this library:
  8. * <code>
  9. * <?php
  10. * include 'System/SSH/Agent.php';
  11. * include 'Net/SSH2.php';
  12. *
  13. * $agent = new System_SSH_Agent();
  14. *
  15. * $ssh = new Net_SSH2('www.domain.tld');
  16. * if (!$ssh->login('username', $agent)) {
  17. * exit('Login Failed');
  18. * }
  19. *
  20. * echo $ssh->exec('pwd');
  21. * echo $ssh->exec('ls -la');
  22. * ?>
  23. * </code>
  24. *
  25. * LICENSE: Permission is hereby granted, free of charge, to any person obtaining a copy
  26. * of this software and associated documentation files (the "Software"), to deal
  27. * in the Software without restriction, including without limitation the rights
  28. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  29. * copies of the Software, and to permit persons to whom the Software is
  30. * furnished to do so, subject to the following conditions:
  31. *
  32. * The above copyright notice and this permission notice shall be included in
  33. * all copies or substantial portions of the Software.
  34. *
  35. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  36. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  37. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  38. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  39. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  40. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  41. * THE SOFTWARE.
  42. *
  43. * @category System
  44. * @package System_SSH_Agent
  45. * @author Jim Wigginton <terrafrost@php.net>
  46. * @copyright 2014 Jim Wigginton
  47. * @license http://www.opensource.org/licenses/mit-license.html MIT License
  48. * @link http://phpseclib.sourceforge.net
  49. * @internal See http://api.libssh.org/rfc/PROTOCOL.agent
  50. */
  51. /**#@+
  52. * Message numbers
  53. *
  54. * @access private
  55. */
  56. // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1)
  57. define('SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES', 11);
  58. // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2).
  59. define('SYSTEM_SSH_AGENT_IDENTITIES_ANSWER', 12);
  60. define('SYSTEM_SSH_AGENT_FAILURE', 5);
  61. // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3)
  62. define('SYSTEM_SSH_AGENTC_SIGN_REQUEST', 13);
  63. // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4)
  64. define('SYSTEM_SSH_AGENT_SIGN_RESPONSE', 14);
  65. /**#@-*/
  66. /**
  67. * Pure-PHP ssh-agent client identity object
  68. *
  69. * Instantiation should only be performed by System_SSH_Agent class.
  70. * This could be thought of as implementing an interface that Crypt_RSA
  71. * implements. ie. maybe a Net_SSH_Auth_PublicKey interface or something.
  72. * The methods in this interface would be getPublicKey, setSignatureMode
  73. * and sign since those are the methods phpseclib looks for to perform
  74. * public key authentication.
  75. *
  76. * @package System_SSH_Agent
  77. * @author Jim Wigginton <terrafrost@php.net>
  78. * @access internal
  79. */
  80. class System_SSH_Agent_Identity
  81. {
  82. /**
  83. * Key Object
  84. *
  85. * @var Crypt_RSA
  86. * @access private
  87. * @see System_SSH_Agent_Identity::getPublicKey()
  88. */
  89. var $key;
  90. /**
  91. * Key Blob
  92. *
  93. * @var String
  94. * @access private
  95. * @see System_SSH_Agent_Identity::sign()
  96. */
  97. var $key_blob;
  98. /**
  99. * Socket Resource
  100. *
  101. * @var Resource
  102. * @access private
  103. * @see System_SSH_Agent_Identity::sign()
  104. */
  105. var $fsock;
  106. /**
  107. * Default Constructor.
  108. *
  109. * @param Resource $fsock
  110. * @return System_SSH_Agent_Identity
  111. * @access private
  112. */
  113. function System_SSH_Agent_Identity($fsock)
  114. {
  115. $this->fsock = $fsock;
  116. }
  117. /**
  118. * Set Public Key
  119. *
  120. * Called by System_SSH_Agent::requestIdentities()
  121. *
  122. * @param Crypt_RSA $key
  123. * @access private
  124. */
  125. function setPublicKey($key)
  126. {
  127. $this->key = $key;
  128. $this->key->setPublicKey();
  129. }
  130. /**
  131. * Set Public Key
  132. *
  133. * Called by System_SSH_Agent::requestIdentities(). The key blob could be extracted from $this->key
  134. * but this saves a small amount of computation.
  135. *
  136. * @param String $key_blob
  137. * @access private
  138. */
  139. function setPublicKeyBlob($key_blob)
  140. {
  141. $this->key_blob = $key_blob;
  142. }
  143. /**
  144. * Get Public Key
  145. *
  146. * Wrapper for $this->key->getPublicKey()
  147. *
  148. * @param Integer $format optional
  149. * @return Mixed
  150. * @access public
  151. */
  152. function getPublicKey($format = null)
  153. {
  154. return !isset($format) ? $this->key->getPublicKey() : $this->key->getPublicKey($format);
  155. }
  156. /**
  157. * Set Signature Mode
  158. *
  159. * Doesn't do anything as ssh-agent doesn't let you pick and choose the signature mode. ie.
  160. * ssh-agent's only supported mode is CRYPT_RSA_SIGNATURE_PKCS1
  161. *
  162. * @param Integer $mode
  163. * @access public
  164. */
  165. function setSignatureMode($mode)
  166. {
  167. }
  168. /**
  169. * Create a signature
  170. *
  171. * See "2.6.2 Protocol 2 private key signature request"
  172. *
  173. * @param String $message
  174. * @return String
  175. * @access public
  176. */
  177. function sign($message)
  178. {
  179. // the last parameter (currently 0) is for flags and ssh-agent only defines one flag (for ssh-dss): SSH_AGENT_OLD_SIGNATURE
  180. $packet = pack('CNa*Na*N', SYSTEM_SSH_AGENTC_SIGN_REQUEST, strlen($this->key_blob), $this->key_blob, strlen($message), $message, 0);
  181. $packet = pack('Na*', strlen($packet), $packet);
  182. if (strlen($packet) != fputs($this->fsock, $packet)) {
  183. user_error('Connection closed during signing');
  184. }
  185. $length = current(unpack('N', fread($this->fsock, 4)));
  186. $type = ord(fread($this->fsock, 1));
  187. if ($type != SYSTEM_SSH_AGENT_SIGN_RESPONSE) {
  188. user_error('Unable to retreive signature');
  189. }
  190. $signature_blob = fread($this->fsock, $length - 1);
  191. // the only other signature format defined - ssh-dss - is the same length as ssh-rsa
  192. // the + 12 is for the other various SSH added length fields
  193. return substr($signature_blob, strlen('ssh-rsa') + 12);
  194. }
  195. }
  196. /**
  197. * Pure-PHP ssh-agent client identity factory
  198. *
  199. * requestIdentities() method pumps out System_SSH_Agent_Identity objects
  200. *
  201. * @package System_SSH_Agent
  202. * @author Jim Wigginton <terrafrost@php.net>
  203. * @access internal
  204. */
  205. class System_SSH_Agent
  206. {
  207. /**
  208. * Socket Resource
  209. *
  210. * @var Resource
  211. * @access private
  212. */
  213. var $fsock;
  214. /**
  215. * Default Constructor
  216. *
  217. * @return System_SSH_Agent
  218. * @access public
  219. */
  220. function System_SSH_Agent()
  221. {
  222. switch (true) {
  223. case isset($_SERVER['SSH_AUTH_SOCK']):
  224. $address = $_SERVER['SSH_AUTH_SOCK'];
  225. break;
  226. case isset($_ENV['SSH_AUTH_SOCK']):
  227. $address = $_ENV['SSH_AUTH_SOCK'];
  228. break;
  229. default:
  230. user_error('SSH_AUTH_SOCK not found');
  231. return false;
  232. }
  233. $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr);
  234. if (!$this->fsock) {
  235. user_error("Unable to connect to ssh-agent (Error $errno: $errstr)");
  236. }
  237. }
  238. /**
  239. * Request Identities
  240. *
  241. * See "2.5.2 Requesting a list of protocol 2 keys"
  242. * Returns an array containing zero or more System_SSH_Agent_Identity objects
  243. *
  244. * @return Array
  245. * @access public
  246. */
  247. function requestIdentities()
  248. {
  249. if (!$this->fsock) {
  250. return array();
  251. }
  252. $packet = pack('NC', 1, SYSTEM_SSH_AGENTC_REQUEST_IDENTITIES);
  253. if (strlen($packet) != fputs($this->fsock, $packet)) {
  254. user_error('Connection closed while requesting identities');
  255. }
  256. $length = current(unpack('N', fread($this->fsock, 4)));
  257. $type = ord(fread($this->fsock, 1));
  258. if ($type != SYSTEM_SSH_AGENT_IDENTITIES_ANSWER) {
  259. user_error('Unable to request identities');
  260. }
  261. $identities = array();
  262. $keyCount = current(unpack('N', fread($this->fsock, 4)));
  263. for ($i = 0; $i < $keyCount; $i++) {
  264. $length = current(unpack('N', fread($this->fsock, 4)));
  265. $key_blob = fread($this->fsock, $length);
  266. $length = current(unpack('N', fread($this->fsock, 4)));
  267. $key_comment = fread($this->fsock, $length);
  268. $length = current(unpack('N', substr($key_blob, 0, 4)));
  269. $key_type = substr($key_blob, 4, $length);
  270. switch ($key_type) {
  271. case 'ssh-rsa':
  272. if (!class_exists('Crypt_RSA')) {
  273. include_once 'Crypt/RSA.php';
  274. }
  275. $key = new Crypt_RSA();
  276. $key->loadKey('ssh-rsa ' . base64_encode($key_blob) . ' ' . $key_comment);
  277. break;
  278. case 'ssh-dss':
  279. // not currently supported
  280. break;
  281. }
  282. // resources are passed by reference by default
  283. if (isset($key)) {
  284. $identity = new System_SSH_Agent_Identity($this->fsock);
  285. $identity->setPublicKey($key);
  286. $identity->setPublicKeyBlob($key_blob);
  287. $identities[] = $identity;
  288. unset($key);
  289. }
  290. }
  291. return $identities;
  292. }
  293. }