[ 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 $this->fsock = fsockopen('unix://' . $address, 0, $errno, $errstr); 137 if (!$this->fsock) { 138 user_error("Unable to connect to ssh-agent (Error $errno: $errstr)"); 139 } 140 } 141 142 /** 143 * Request Identities 144 * 145 * See "2.5.2 Requesting a list of protocol 2 keys" 146 * Returns an array containing zero or more \phpseclib\System\SSH\Agent\Identity objects 147 * 148 * @return array 149 * @access public 150 */ 151 function requestIdentities() 152 { 153 if (!$this->fsock) { 154 return array(); 155 } 156 157 $packet = pack('NC', 1, self::SSH_AGENTC_REQUEST_IDENTITIES); 158 if (strlen($packet) != fputs($this->fsock, $packet)) { 159 user_error('Connection closed while requesting identities'); 160 return array(); 161 } 162 163 $temp = fread($this->fsock, 4); 164 if (strlen($temp) != 4) { 165 user_error('Connection closed while requesting identities'); 166 return array(); 167 } 168 $length = current(unpack('N', $temp)); 169 $type = ord(fread($this->fsock, 1)); 170 if ($type != self::SSH_AGENT_IDENTITIES_ANSWER) { 171 user_error('Unable to request identities'); 172 return array(); 173 } 174 175 $identities = array(); 176 $temp = fread($this->fsock, 4); 177 if (strlen($temp) != 4) { 178 user_error('Connection closed while requesting identities'); 179 return array(); 180 } 181 $keyCount = current(unpack('N', $temp)); 182 for ($i = 0; $i < $keyCount; $i++) { 183 $temp = fread($this->fsock, 4); 184 if (strlen($temp) != 4) { 185 user_error('Connection closed while requesting identities'); 186 return array(); 187 } 188 $length = current(unpack('N', $temp)); 189 $key_blob = fread($this->fsock, $length); 190 if (strlen($key_blob) != $length) { 191 user_error('Connection closed while requesting identities'); 192 return array(); 193 } 194 $key_str = 'ssh-rsa ' . base64_encode($key_blob); 195 $temp = fread($this->fsock, 4); 196 if (strlen($temp) != 4) { 197 user_error('Connection closed while requesting identities'); 198 return array(); 199 } 200 $length = current(unpack('N', $temp)); 201 if ($length) { 202 $temp = fread($this->fsock, $length); 203 if (strlen($temp) != $length) { 204 user_error('Connection closed while requesting identities'); 205 return array(); 206 } 207 $key_str.= ' ' . $temp; 208 } 209 $length = current(unpack('N', substr($key_blob, 0, 4))); 210 $key_type = substr($key_blob, 4, $length); 211 switch ($key_type) { 212 case 'ssh-rsa': 213 $key = new RSA(); 214 $key->loadKey($key_str); 215 break; 216 case 'ssh-dss': 217 // not currently supported 218 break; 219 } 220 // resources are passed by reference by default 221 if (isset($key)) { 222 $identity = new Identity($this->fsock); 223 $identity->setPublicKey($key); 224 $identity->setPublicKeyBlob($key_blob); 225 $identities[] = $identity; 226 unset($key); 227 } 228 } 229 230 return $identities; 231 } 232 233 /** 234 * Signal that agent forwarding should 235 * be requested when a channel is opened 236 * 237 * @param Net_SSH2 $ssh 238 * @return bool 239 * @access public 240 */ 241 function startSSHForwarding($ssh) 242 { 243 if ($this->forward_status == self::FORWARD_NONE) { 244 $this->forward_status = self::FORWARD_REQUEST; 245 } 246 } 247 248 /** 249 * Request agent forwarding of remote server 250 * 251 * @param Net_SSH2 $ssh 252 * @return bool 253 * @access private 254 */ 255 function _request_forwarding($ssh) 256 { 257 $request_channel = $ssh->_get_open_channel(); 258 if ($request_channel === false) { 259 return false; 260 } 261 262 $packet = pack( 263 'CNNa*C', 264 NET_SSH2_MSG_CHANNEL_REQUEST, 265 $ssh->server_channels[$request_channel], 266 strlen('auth-agent-req@openssh.com'), 267 'auth-agent-req@openssh.com', 268 1 269 ); 270 271 $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; 272 273 if (!$ssh->_send_binary_packet($packet)) { 274 return false; 275 } 276 277 $response = $ssh->_get_channel_packet($request_channel); 278 if ($response === false) { 279 return false; 280 } 281 282 $ssh->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; 283 $this->forward_status = self::FORWARD_ACTIVE; 284 285 return true; 286 } 287 288 /** 289 * On successful channel open 290 * 291 * This method is called upon successful channel 292 * open to give the SSH Agent an opportunity 293 * to take further action. i.e. request agent forwarding 294 * 295 * @param Net_SSH2 $ssh 296 * @access private 297 */ 298 function _on_channel_open($ssh) 299 { 300 if ($this->forward_status == self::FORWARD_REQUEST) { 301 $this->_request_forwarding($ssh); 302 } 303 } 304 305 /** 306 * Forward data to SSH Agent and return data reply 307 * 308 * @param string $data 309 * @return data from SSH Agent 310 * @access private 311 */ 312 function _forward_data($data) 313 { 314 if ($this->expected_bytes > 0) { 315 $this->socket_buffer.= $data; 316 $this->expected_bytes -= strlen($data); 317 } else { 318 $agent_data_bytes = current(unpack('N', $data)); 319 $current_data_bytes = strlen($data); 320 $this->socket_buffer = $data; 321 if ($current_data_bytes != $agent_data_bytes + 4) { 322 $this->expected_bytes = ($agent_data_bytes + 4) - $current_data_bytes; 323 return false; 324 } 325 } 326 327 if (strlen($this->socket_buffer) != fwrite($this->fsock, $this->socket_buffer)) { 328 user_error('Connection closed attempting to forward data to SSH agent'); 329 return false; 330 } 331 332 $this->socket_buffer = ''; 333 $this->expected_bytes = 0; 334 335 $temp = fread($this->fsock, 4); 336 if (strlen($temp) != 4) { 337 user_error('Connection closed while reading data response'); 338 return false; 339 } 340 $agent_reply_bytes = current(unpack('N', $temp)); 341 342 $agent_reply_data = fread($this->fsock, $agent_reply_bytes); 343 if (strlen($agent_reply_data) != $agent_reply_bytes) { 344 user_error('Connection closed while reading data response'); 345 return false; 346 } 347 $agent_reply_data = current(unpack('a*', $agent_reply_data)); 348 349 return pack('Na*', $agent_reply_bytes, $agent_reply_data); 350 } 351 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body