[ Index ] |
PHP Cross Reference of DokuWiki |
[Summary view] [Print] [Text view]
1 <?php 2 3 /** 4 * Pure-PHP ssh-agent client. 5 * 6 * PHP version 5 7 * 8 * Here are some examples of how to use this library: 9 * <code> 10 * <?php 11 * include 'vendor/autoload.php'; 12 * 13 * $agent = new \phpseclib\System\SSH\Agent(); 14 * 15 * $ssh = new \phpseclib\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 * @category System 26 * @package SSH\Agent 27 * @author Jim Wigginton <terrafrost@php.net> 28 * @copyright 2014 Jim Wigginton 29 * @license http://www.opensource.org/licenses/mit-license.html MIT License 30 * @link http://phpseclib.sourceforge.net 31 * @internal See http://api.libssh.org/rfc/PROTOCOL.agent 32 */ 33 34 namespace phpseclib\System\SSH; 35 36 use phpseclib\Crypt\RSA; 37 use phpseclib\System\SSH\Agent\Identity; 38 39 /** 40 * Pure-PHP ssh-agent client identity factory 41 * 42 * requestIdentities() method pumps out \phpseclib\System\SSH\Agent\Identity objects 43 * 44 * @package SSH\Agent 45 * @author Jim Wigginton <terrafrost@php.net> 46 * @access public 47 */ 48 class Agent 49 { 50 /**#@+ 51 * Message numbers 52 * 53 * @access private 54 */ 55 // to request SSH1 keys you have to use SSH_AGENTC_REQUEST_RSA_IDENTITIES (1) 56 const SSH_AGENTC_REQUEST_IDENTITIES = 11; 57 // this is the SSH2 response; the SSH1 response is SSH_AGENT_RSA_IDENTITIES_ANSWER (2). 58 const SSH_AGENT_IDENTITIES_ANSWER = 12; 59 // the SSH1 request is SSH_AGENTC_RSA_CHALLENGE (3) 60 const SSH_AGENTC_SIGN_REQUEST = 13; 61 // the SSH1 response is SSH_AGENT_RSA_RESPONSE (4) 62 const SSH_AGENT_SIGN_RESPONSE = 14; 63 /**#@-*/ 64 65 /**@+ 66 * Agent forwarding status 67 * 68 * @access private 69 */ 70 // no forwarding requested and not active 71 const FORWARD_NONE = 0; 72 // request agent forwarding when opportune 73 const FORWARD_REQUEST = 1; 74 // forwarding has been request and is active 75 const FORWARD_ACTIVE = 2; 76 /**#@-*/ 77 78 /** 79 * Unused 80 */ 81 const SSH_AGENT_FAILURE = 5; 82 83 /** 84 * Socket Resource 85 * 86 * @var resource 87 * @access private 88 */ 89 var $fsock; 90 91 /** 92 * Agent forwarding status 93 * 94 * @access private 95 */ 96 var $forward_status = self::FORWARD_NONE; 97 98 /** 99 * Buffer for accumulating forwarded authentication 100 * agent data arriving on SSH data channel destined 101 * for agent unix socket 102 * 103 * @access private 104 */ 105 var $socket_buffer = ''; 106 107 /** 108 * Tracking the number of bytes we are expecting 109 * to arrive for the agent socket on the SSH data 110 * channel 111 */ 112 var $expected_bytes = 0; 113 114 /** 115 * Default Constructor 116 * 117 * @return \phpseclib\System\SSH\Agent 118 * @access public 119 */ 120 function __construct($address = null) 121 { 122 if (!$address) { 123 switch (true) { 124 case isset($_SERVER['SSH_AUTH_SOCK']): 125 $address = $_SERVER['SSH_AUTH_SOCK']; 126 break; 127 case isset($_ENV['SSH_AUTH_SOCK']): 128 $address = $_ENV['SSH_AUTH_SOCK']; 129 break; 130 default: 131 user_error('SSH_AUTH_SOCK not found'); 132 return false; 133 } 134 } 135 136 if (in_array('unix', stream_get_transports())) { 137 $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); 138 if (!$this->fsock) { 139 user_error("Unable to connect to ssh-agent (Error $errno: $errstr)"); 140 } 141 } else { 142 if (substr($address, 0, 9) != '\\\\.\\pipe\\' || strpos(substr($address, 9), '\\') !== false) { 143 user_error('Address is not formatted as a named pipe should be'); 144 } else { 145 $this->fsock = fopen($address, 'r+b'); 146 if (!$this->fsock) { 147 user_error('Unable to open address'); 148 } 149 } 150 } 151 } 152 153 /** 154 * Request Identities 155 * 156 * See "2.5.2 Requesting a list of protocol 2 keys" 157 * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects 158 * 159 * @return array 160 * @access public 161 */ 162 function requestIdentities() 163 { 164 if (!$this->fsock) { 165 return array(); 166 } 167 168 $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); 169 if (strlen($packet) != fputs($this->fsock, $packet)) { 170 user_error('Connection closed while requesting identities'); 171 return array(); 172 } 173 174 $temp = fread($this->fsock, 4); 175 if (strlen($temp) != 4) { 176 user_error('Connection closed while requesting identities'); 177 return array(); 178 } 179 $length = current(unpack('N', $temp)); 180 $type = ord(fread($this->fsock, 1)); 181 if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { 182 user_error('Unable to request identities'); 183 return array(); 184 } 185 186 $identities = array(); 187 $temp = fread($this->fsock, 4); 188 if (strlen($temp) != 4) { 189 user_error('Connection closed while requesting identities'); 190 return array(); 191 } 192 $keyCount = current(unpack('N', $temp)); 193 for ($i = 0; $i < $keyCount; $i++) { 194 $temp = fread($this->fsock, 4); 195 if (strlen($temp) != 4) { 196 user_error('Connection closed while requesting identities'); 197 return array(); 198 } 199 $length = current(unpack('N', $temp)); 200 $key_blob = fread($this->fsock, $length); 201 if (strlen($key_blob) != $length) { 202 user_error('Connection closed while requesting identities'); 203 return array(); 204 } 205 $key_str = 'ssh-rsa ' . base64_encode($key_blob); 206 $temp = fread($this->fsock, 4); 207 if (strlen($temp) != 4) { 208 user_error('Connection closed while requesting identities'); 209 return array(); 210 } 211 $length = current(unpack('N', $temp)); 212 if ($length) { 213 $temp = fread($this->fsock, $length); 214 if (strlen($temp) != $length) { 215 user_error('Connection closed while requesting identities'); 216 return array(); 217 } 218 $key_str.= ' ' . $temp; 219 } 220 $length = current(unpack('N', substr($key_blob, 0, 4))); 221 $key_type = substr($key_blob, 4, $length); 222 switch ($key_type) { 223 case 'ssh-rsa': 224 $key = new RSA(); 225 $key->loadKey($key_str); 226 break; 227 case 'ssh-dss': 228 // not currently supported 229 break; 230 } 231 // resources are passed by reference by default 232 if (isset($key)) { 233 $identity = new Identity($this->fsock); 234 $identity->setPublicKey($key); 235 $identity->setPublicKeyBlob($key_blob); 236 $identities[] = $identity; 237 unset($key); 238 } 239 } 240 241 return $identities; 242 } 243 244 /** 245 * Signal that agent forwarding should 246 * be requested when a channel is opened 247 * 248 * @return bool 249 * @access public 250 */ 251 function startSSHForwarding() 252 { 253 if ($this->forward_status == self::FORWARD_NONE) { 254 $this->forward_status = self::FORWARD_REQUEST; 255 } 256 } 257 258 /** 259 * Request agent forwarding of remote server 260 * 261 * @param Net_SSH2 $ssh 262 * @return bool 263 * @access private 264 */ 265 function _request_forwarding($ssh) 266 { 267 $request_channel = $ssh->_get_open_channel(); 268 if ($request_channel === false) { 269 return false; 270 } 271 272 $packet = pack( 273 'CNNa*C', 274 NET_SSH2_MSG_CHANNEL_REQUEST, 275 $ssh->server_channels[$request_channel], 276 strlen('auth-agent-req@openssh.com'), 277 'auth-agent-req@openssh.com', 278 1 279 ); 280 281 $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; 282 283 if (!$ssh->_send_binary_packet($packet)) { 284 return false; 285 } 286 287 $response = $ssh->_get_channel_packet($request_channel); 288 if ($response === false) { 289 return false; 290 } 291 292 $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; 293 $this->forward_status = self::FORWARD_ACTIVE; 294 295 return true; 296 } 297 298 /** 299 * On successful channel open 300 * 301 * This method is called upon successful channel 302 * open to give the SSH Agent an opportunity 303 * to take further action. i.e. request agent forwarding 304 * 305 * @param Net_SSH2 $ssh 306 * @access private 307 */ 308 function _on_channel_open($ssh) 309 { 310 if ($this->forward_status == self::FORWARD_REQUEST) { 311 $this->_request_forwarding($ssh); 312 } 313 } 314 315 /** 316 * Forward data to SSH Agent and return data reply 317 * 318 * @param string $data 319 * @return data from SSH Agent 320 * @access private 321 */ 322 function _forward_data($data) 323 { 324 if ($this->expected_bytes > 0) { 325 $this->socket_buffer.= $data; 326 $this->expected_bytes -= strlen($data); 327 } else { 328 $agent_data_bytes = current(unpack('N', $data)); 329 $current_data_bytes = strlen($data); 330 $this->socket_buffer = $data; 331 if ($current_data_bytes != $agent_data_bytes + 4) { 332 $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes; 333 return false; 334 } 335 } 336 337 if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { 338 user_error('Connection closed attempting to forward data to SSH agent'); 339 return false; 340 } 341 342 $this->socket_buffer = ''; 343 $this->expected_bytes = 0; 344 345 $temp = fread($this->fsock, 4); 346 if (strlen($temp) != 4) { 347 user_error('Connection closed while reading data response'); 348 return false; 349 } 350 $agent_reply_bytes = current(unpack('N', $temp)); 351 352 $agent_reply_data = fread($this->fsock, $agent_reply_bytes); 353 if (strlen($agent_reply_data) != $agent_reply_bytes) { 354 user_error('Connection closed while reading data response'); 355 return false; 356 } 357 $agent_reply_data = current(unpack('a*', $agent_reply_data)); 358 359 return pack('Na*', $agent_reply_bytes, $agent_reply_data); 360 } 361 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body