Rev 846 | Rev 1042 | Go to most recent revision | Show entire file | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 846 | Rev 874 | ||
---|---|---|---|
Line 35... | Line 35... | ||
35 | * $ssh->write("ls -la\n"); |
35 | * $ssh->write("ls -la\n"); |
36 | * echo $ssh->read('username@username:~$'); |
36 | * echo $ssh->read('username@username:~$'); |
37 | * ?> |
37 | * ?> |
38 | * </code> |
38 | * </code> |
39 | * |
39 | * |
- | 40 | * @category Net |
|
- | 41 | * @package SSH2 |
|
40 | * @author Jim Wigginton <terrafrost@php.net> |
42 | * @author Jim Wigginton <terrafrost@php.net> |
41 | * @copyright 2007 Jim Wigginton |
43 | * @copyright 2007 Jim Wigginton |
42 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
44 | * @license http://www.opensource.org/licenses/mit-license.html MIT License |
43 | * @link http://phpseclib.sourceforge.net |
45 | * @link http://phpseclib.sourceforge.net |
44 | */ |
46 | */ |
Line 58... | Line 60... | ||
58 | use phpseclib3\Crypt\Hash; |
60 | use phpseclib3\Crypt\Hash; |
59 | use phpseclib3\Crypt\Random; |
61 | use phpseclib3\Crypt\Random; |
60 | use phpseclib3\Crypt\RC4; |
62 | use phpseclib3\Crypt\RC4; |
61 | use phpseclib3\Crypt\Rijndael; |
63 | use phpseclib3\Crypt\Rijndael; |
62 | use phpseclib3\Crypt\RSA; |
64 | use phpseclib3\Crypt\RSA; |
63 | use phpseclib3\Crypt\TripleDES; |
65 | use phpseclib3\Crypt\TripleDES; // Used to do Diffie-Hellman key exchange and DSA/RSA signature verification. |
64 | use phpseclib3\Crypt\Twofish; |
66 | use phpseclib3\Crypt\Twofish; |
65 | use phpseclib3\Exception\ConnectionClosedException; |
67 | use phpseclib3\Exception\ConnectionClosedException; |
66 | use phpseclib3\Exception\InsufficientSetupException; |
68 | use phpseclib3\Exception\InsufficientSetupException; |
67 | use phpseclib3\Exception\NoSupportedAlgorithmsException; |
69 | use phpseclib3\Exception\NoSupportedAlgorithmsException; |
68 | use phpseclib3\Exception\UnableToConnectException; |
70 | use phpseclib3\Exception\UnableToConnectException; |
69 | use phpseclib3\Exception\UnsupportedAlgorithmException; |
71 | use phpseclib3\Exception\UnsupportedAlgorithmException; |
70 | use phpseclib3\Exception\UnsupportedCurveException; |
72 | use phpseclib3\Exception\UnsupportedCurveException; |
71 | use phpseclib3\Math\BigInteger; |
73 | use phpseclib3\Math\BigInteger; |
72 | use phpseclib3\Net\SSH2\ChannelConnectionFailureReason; |
- | |
73 | use phpseclib3\Net\SSH2\DisconnectReason; |
- | |
74 | use phpseclib3\Net\SSH2\MessageType; |
- | |
75 | use phpseclib3\Net\SSH2\MessageTypeExtra; |
- | |
76 | use phpseclib3\Net\SSH2\TerminalMode; |
- | |
77 | use phpseclib3\System\SSH\Agent; |
74 | use phpseclib3\System\SSH\Agent; |
78 | 75 | ||
79 | /** |
76 | /** |
80 | * Pure-PHP implementation of SSHv2. |
77 | * Pure-PHP implementation of SSHv2. |
81 | * |
78 | * |
- | 79 | * @package SSH2 |
|
82 | * @author Jim Wigginton <terrafrost@php.net> |
80 | * @author Jim Wigginton <terrafrost@php.net> |
- | 81 | * @access public |
|
83 | */ |
82 | */ |
84 | class SSH2 |
83 | class SSH2 |
85 | { |
84 | { |
86 | /**#@+ |
85 | /**#@+ |
87 | * Compression Types |
86 | * Compression Types |
88 | * |
87 | * |
- | 88 | * @access private |
|
89 | */ |
89 | */ |
90 | /** |
90 | /** |
91 | * No compression |
91 | * No compression |
92 | */ |
92 | */ |
93 | const NET_SSH2_COMPRESSION_NONE = 1; |
93 | const NET_SSH2_COMPRESSION_NONE = 1; |
Line 121... | Line 121... | ||
121 | * open request, and 'sender channel' is the channel number allocated by |
121 | * open request, and 'sender channel' is the channel number allocated by |
122 | * the other side. |
122 | * the other side. |
123 | * |
123 | * |
124 | * @see \phpseclib3\Net\SSH2::send_channel_packet() |
124 | * @see \phpseclib3\Net\SSH2::send_channel_packet() |
125 | * @see \phpseclib3\Net\SSH2::get_channel_packet() |
125 | * @see \phpseclib3\Net\SSH2::get_channel_packet() |
- | 126 | * @access private |
|
126 | */ |
127 | */ |
127 | const CHANNEL_EXEC = 1; // PuTTy uses 0x100 |
128 | const CHANNEL_EXEC = 1; // PuTTy uses 0x100 |
128 | const CHANNEL_SHELL = 2; |
129 | const CHANNEL_SHELL = 2; |
129 | const CHANNEL_SUBSYSTEM = 3; |
130 | const CHANNEL_SUBSYSTEM = 3; |
130 | const CHANNEL_AGENT_FORWARD = 4; |
131 | const CHANNEL_AGENT_FORWARD = 4; |
131 | const CHANNEL_KEEP_ALIVE = 5; |
132 | const CHANNEL_KEEP_ALIVE = 5; |
132 | 133 | ||
133 | /** |
134 | /** |
134 | * Returns the message numbers |
135 | * Returns the message numbers |
135 | * |
136 | * |
- | 137 | * @access public |
|
136 | * @see \phpseclib3\Net\SSH2::getLog() |
138 | * @see \phpseclib3\Net\SSH2::getLog() |
137 | */ |
139 | */ |
138 | const LOG_SIMPLE = 1; |
140 | const LOG_SIMPLE = 1; |
139 | /** |
141 | /** |
140 | * Returns the message content |
142 | * Returns the message content |
141 | * |
143 | * |
- | 144 | * @access public |
|
142 | * @see \phpseclib3\Net\SSH2::getLog() |
145 | * @see \phpseclib3\Net\SSH2::getLog() |
143 | */ |
146 | */ |
144 | const LOG_COMPLEX = 2; |
147 | const LOG_COMPLEX = 2; |
145 | /** |
148 | /** |
146 | * Outputs the content real-time |
149 | * Outputs the content real-time |
147 | * |
150 | * |
- | 151 | * @access public |
|
148 | * @see \phpseclib3\Net\SSH2::getLog() |
152 | * @see \phpseclib3\Net\SSH2::getLog() |
149 | */ |
153 | */ |
150 | const LOG_REALTIME = 3; |
154 | const LOG_REALTIME = 3; |
151 | /** |
155 | /** |
152 | * Dumps the content real-time to a file |
156 | * Dumps the content real-time to a file |
153 | * |
157 | * |
- | 158 | * @access public |
|
154 | * @see \phpseclib3\Net\SSH2::getLog() |
159 | * @see \phpseclib3\Net\SSH2::getLog() |
155 | */ |
160 | */ |
156 | const LOG_REALTIME_FILE = 4; |
161 | const LOG_REALTIME_FILE = 4; |
157 | /** |
162 | /** |
158 | * Make sure that the log never gets larger than this |
163 | * Make sure that the log never gets larger than this |
159 | * |
164 | * |
- | 165 | * @access public |
|
160 | * @see \phpseclib3\Net\SSH2::getLog() |
166 | * @see \phpseclib3\Net\SSH2::getLog() |
161 | */ |
167 | */ |
162 | const LOG_MAX_SIZE = 1048576; // 1024 * 1024 |
168 | const LOG_MAX_SIZE = 1048576; // 1024 * 1024 |
163 | 169 | ||
164 | /** |
170 | /** |
165 | * Returns when a string matching $expect exactly is found |
171 | * Returns when a string matching $expect exactly is found |
166 | * |
172 | * |
- | 173 | * @access public |
|
167 | * @see \phpseclib3\Net\SSH2::read() |
174 | * @see \phpseclib3\Net\SSH2::read() |
168 | */ |
175 | */ |
169 | const READ_SIMPLE = 1; |
176 | const READ_SIMPLE = 1; |
170 | /** |
177 | /** |
171 | * Returns when a string matching the regular expression $expect is found |
178 | * Returns when a string matching the regular expression $expect is found |
172 | * |
179 | * |
- | 180 | * @access public |
|
173 | * @see \phpseclib3\Net\SSH2::read() |
181 | * @see \phpseclib3\Net\SSH2::read() |
174 | */ |
182 | */ |
175 | const READ_REGEX = 2; |
183 | const READ_REGEX = 2; |
176 | /** |
184 | /** |
177 | * Returns whenever a data packet is received. |
185 | * Returns whenever a data packet is received. |
178 | * |
186 | * |
179 | * Some data packets may only contain a single character so it may be necessary |
187 | * Some data packets may only contain a single character so it may be necessary |
180 | * to call read() multiple times when using this option |
188 | * to call read() multiple times when using this option |
181 | * |
189 | * |
- | 190 | * @access public |
|
182 | * @see \phpseclib3\Net\SSH2::read() |
191 | * @see \phpseclib3\Net\SSH2::read() |
183 | */ |
192 | */ |
184 | const READ_NEXT = 3; |
193 | const READ_NEXT = 3; |
185 | 194 | ||
186 | /** |
195 | /** |
187 | * The SSH identifier |
196 | * The SSH identifier |
188 | * |
197 | * |
189 | * @var string |
198 | * @var string |
- | 199 | * @access private |
|
190 | */ |
200 | */ |
191 | private $identifier; |
201 | private $identifier; |
192 | 202 | ||
193 | /** |
203 | /** |
194 | * The Socket Object |
204 | * The Socket Object |
195 | * |
205 | * |
196 | * @var resource|closed-resource|null |
206 | * @var resource|closed-resource|null |
- | 207 | * @access private |
|
197 | */ |
208 | */ |
198 | public $fsock; |
209 | public $fsock; |
199 | 210 | ||
200 | /** |
211 | /** |
201 | * Execution Bitmap |
212 | * Execution Bitmap |
202 | * |
213 | * |
203 | * The bits that are set represent functions that have been called already. This is used to determine |
214 | * The bits that are set represent functions that have been called already. This is used to determine |
204 | * if a requisite function has been successfully executed. If not, an error should be thrown. |
215 | * if a requisite function has been successfully executed. If not, an error should be thrown. |
205 | * |
216 | * |
206 | * @var int |
217 | * @var int |
- | 218 | * @access private |
|
207 | */ |
219 | */ |
208 | protected $bitmap = 0; |
220 | protected $bitmap = 0; |
209 | 221 | ||
210 | /** |
222 | /** |
211 | * Error information |
223 | * Error information |
212 | * |
224 | * |
213 | * @see self::getErrors() |
225 | * @see self::getErrors() |
214 | * @see self::getLastError() |
226 | * @see self::getLastError() |
215 | * @var array |
227 | * @var array |
- | 228 | * @access private |
|
216 | */ |
229 | */ |
217 | private $errors = []; |
230 | private $errors = []; |
218 | 231 | ||
219 | /** |
232 | /** |
220 | * Server Identifier |
233 | * Server Identifier |
221 | * |
234 | * |
222 | * @see self::getServerIdentification() |
235 | * @see self::getServerIdentification() |
223 | * @var string|false |
236 | * @var string|false |
- | 237 | * @access private |
|
224 | */ |
238 | */ |
225 | protected $server_identifier = false; |
239 | protected $server_identifier = false; |
226 | 240 | ||
227 | /** |
241 | /** |
228 | * Key Exchange Algorithms |
242 | * Key Exchange Algorithms |
229 | * |
243 | * |
230 | * @see self::getKexAlgorithims() |
244 | * @see self::getKexAlgorithims() |
231 | * @var array|false |
245 | * @var array|false |
- | 246 | * @access private |
|
232 | */ |
247 | */ |
233 | private $kex_algorithms = false; |
248 | private $kex_algorithms = false; |
234 | 249 | ||
235 | /** |
250 | /** |
236 | * Key Exchange Algorithm |
251 | * Key Exchange Algorithm |
237 | * |
252 | * |
238 | * @see self::getMethodsNegotiated() |
253 | * @see self::getMethodsNegotiated() |
239 | * @var string|false |
254 | * @var string|false |
- | 255 | * @access private |
|
240 | */ |
256 | */ |
241 | private $kex_algorithm = false; |
257 | private $kex_algorithm = false; |
242 | 258 | ||
243 | /** |
259 | /** |
244 | * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods |
260 | * Minimum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods |
245 | * |
261 | * |
246 | * @see self::_key_exchange() |
262 | * @see self::_key_exchange() |
247 | * @var int |
263 | * @var int |
- | 264 | * @access private |
|
248 | */ |
265 | */ |
249 | private $kex_dh_group_size_min = 1536; |
266 | private $kex_dh_group_size_min = 1536; |
250 | 267 | ||
251 | /** |
268 | /** |
252 | * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods |
269 | * Preferred Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods |
253 | * |
270 | * |
254 | * @see self::_key_exchange() |
271 | * @see self::_key_exchange() |
255 | * @var int |
272 | * @var int |
- | 273 | * @access private |
|
256 | */ |
274 | */ |
257 | private $kex_dh_group_size_preferred = 2048; |
275 | private $kex_dh_group_size_preferred = 2048; |
258 | 276 | ||
259 | /** |
277 | /** |
260 | * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods |
278 | * Maximum Diffie-Hellman Group Bit Size in RFC 4419 Key Exchange Methods |
261 | * |
279 | * |
262 | * @see self::_key_exchange() |
280 | * @see self::_key_exchange() |
263 | * @var int |
281 | * @var int |
- | 282 | * @access private |
|
264 | */ |
283 | */ |
265 | private $kex_dh_group_size_max = 4096; |
284 | private $kex_dh_group_size_max = 4096; |
266 | 285 | ||
267 | /** |
286 | /** |
268 | * Server Host Key Algorithms |
287 | * Server Host Key Algorithms |
269 | * |
288 | * |
270 | * @see self::getServerHostKeyAlgorithms() |
289 | * @see self::getServerHostKeyAlgorithms() |
271 | * @var array|false |
290 | * @var array|false |
- | 291 | * @access private |
|
272 | */ |
292 | */ |
273 | private $server_host_key_algorithms = false; |
293 | private $server_host_key_algorithms = false; |
274 | 294 | ||
275 | /** |
295 | /** |
276 | * Encryption Algorithms: Client to Server |
296 | * Encryption Algorithms: Client to Server |
277 | * |
297 | * |
278 | * @see self::getEncryptionAlgorithmsClient2Server() |
298 | * @see self::getEncryptionAlgorithmsClient2Server() |
279 | * @var array|false |
299 | * @var array|false |
- | 300 | * @access private |
|
280 | */ |
301 | */ |
281 | private $encryption_algorithms_client_to_server = false; |
302 | private $encryption_algorithms_client_to_server = false; |
282 | 303 | ||
283 | /** |
304 | /** |
284 | * Encryption Algorithms: Server to Client |
305 | * Encryption Algorithms: Server to Client |
285 | * |
306 | * |
286 | * @see self::getEncryptionAlgorithmsServer2Client() |
307 | * @see self::getEncryptionAlgorithmsServer2Client() |
287 | * @var array|false |
308 | * @var array|false |
- | 309 | * @access private |
|
288 | */ |
310 | */ |
289 | private $encryption_algorithms_server_to_client = false; |
311 | private $encryption_algorithms_server_to_client = false; |
290 | 312 | ||
291 | /** |
313 | /** |
292 | * MAC Algorithms: Client to Server |
314 | * MAC Algorithms: Client to Server |
293 | * |
315 | * |
294 | * @see self::getMACAlgorithmsClient2Server() |
316 | * @see self::getMACAlgorithmsClient2Server() |
295 | * @var array|false |
317 | * @var array|false |
- | 318 | * @access private |
|
296 | */ |
319 | */ |
297 | private $mac_algorithms_client_to_server = false; |
320 | private $mac_algorithms_client_to_server = false; |
298 | 321 | ||
299 | /** |
322 | /** |
300 | * MAC Algorithms: Server to Client |
323 | * MAC Algorithms: Server to Client |
301 | * |
324 | * |
302 | * @see self::getMACAlgorithmsServer2Client() |
325 | * @see self::getMACAlgorithmsServer2Client() |
303 | * @var array|false |
326 | * @var array|false |
- | 327 | * @access private |
|
304 | */ |
328 | */ |
305 | private $mac_algorithms_server_to_client = false; |
329 | private $mac_algorithms_server_to_client = false; |
306 | 330 | ||
307 | /** |
331 | /** |
308 | * Compression Algorithms: Client to Server |
332 | * Compression Algorithms: Client to Server |
309 | * |
333 | * |
310 | * @see self::getCompressionAlgorithmsClient2Server() |
334 | * @see self::getCompressionAlgorithmsClient2Server() |
311 | * @var array|false |
335 | * @var array|false |
- | 336 | * @access private |
|
312 | */ |
337 | */ |
313 | private $compression_algorithms_client_to_server = false; |
338 | private $compression_algorithms_client_to_server = false; |
314 | 339 | ||
315 | /** |
340 | /** |
316 | * Compression Algorithms: Server to Client |
341 | * Compression Algorithms: Server to Client |
317 | * |
342 | * |
318 | * @see self::getCompressionAlgorithmsServer2Client() |
343 | * @see self::getCompressionAlgorithmsServer2Client() |
319 | * @var array|false |
344 | * @var array|false |
- | 345 | * @access private |
|
320 | */ |
346 | */ |
321 | private $compression_algorithms_server_to_client = false; |
347 | private $compression_algorithms_server_to_client = false; |
322 | 348 | ||
323 | /** |
349 | /** |
324 | * Languages: Server to Client |
350 | * Languages: Server to Client |
325 | * |
351 | * |
326 | * @see self::getLanguagesServer2Client() |
352 | * @see self::getLanguagesServer2Client() |
327 | * @var array|false |
353 | * @var array|false |
- | 354 | * @access private |
|
328 | */ |
355 | */ |
329 | private $languages_server_to_client = false; |
356 | private $languages_server_to_client = false; |
330 | 357 | ||
331 | /** |
358 | /** |
332 | * Languages: Client to Server |
359 | * Languages: Client to Server |
333 | * |
360 | * |
334 | * @see self::getLanguagesClient2Server() |
361 | * @see self::getLanguagesClient2Server() |
335 | * @var array|false |
362 | * @var array|false |
- | 363 | * @access private |
|
336 | */ |
364 | */ |
337 | private $languages_client_to_server = false; |
365 | private $languages_client_to_server = false; |
338 | 366 | ||
339 | /** |
367 | /** |
340 | * Preferred Algorithms |
368 | * Preferred Algorithms |
341 | * |
369 | * |
342 | * @see self::setPreferredAlgorithms() |
370 | * @see self::setPreferredAlgorithms() |
343 | * @var array |
371 | * @var array |
- | 372 | * @access private |
|
344 | */ |
373 | */ |
345 | private $preferred = []; |
374 | private $preferred = []; |
346 | 375 | ||
347 | /** |
376 | /** |
348 | * Block Size for Server to Client Encryption |
377 | * Block Size for Server to Client Encryption |
Line 355... | Line 384... | ||
355 | * -- http://tools.ietf.org/html/rfc4253#section-6 |
384 | * -- http://tools.ietf.org/html/rfc4253#section-6 |
356 | * |
385 | * |
357 | * @see self::__construct() |
386 | * @see self::__construct() |
358 | * @see self::_send_binary_packet() |
387 | * @see self::_send_binary_packet() |
359 | * @var int |
388 | * @var int |
- | 389 | * @access private |
|
360 | */ |
390 | */ |
361 | private $encrypt_block_size = 8; |
391 | private $encrypt_block_size = 8; |
362 | 392 | ||
363 | /** |
393 | /** |
364 | * Block Size for Client to Server Encryption |
394 | * Block Size for Client to Server Encryption |
365 | * |
395 | * |
366 | * @see self::__construct() |
396 | * @see self::__construct() |
367 | * @see self::_get_binary_packet() |
397 | * @see self::_get_binary_packet() |
368 | * @var int |
398 | * @var int |
- | 399 | * @access private |
|
369 | */ |
400 | */ |
370 | private $decrypt_block_size = 8; |
401 | private $decrypt_block_size = 8; |
371 | 402 | ||
372 | /** |
403 | /** |
373 | * Server to Client Encryption Object |
404 | * Server to Client Encryption Object |
374 | * |
405 | * |
375 | * @see self::_get_binary_packet() |
406 | * @see self::_get_binary_packet() |
376 | * @var SymmetricKey|false |
407 | * @var SymmetricKey|false |
- | 408 | * @access private |
|
377 | */ |
409 | */ |
378 | private $decrypt = false; |
410 | private $decrypt = false; |
379 | 411 | ||
380 | /** |
412 | /** |
381 | * Decryption Algorithm Name |
413 | * Decryption Algorithm Name |
382 | * |
414 | * |
383 | * @var string|null |
415 | * @var string|null |
- | 416 | * @access private |
|
384 | */ |
417 | */ |
385 | private $decryptName; |
418 | private $decryptName; |
386 | 419 | ||
387 | /** |
420 | /** |
388 | * Decryption Invocation Counter |
421 | * Decryption Invocation Counter |
389 | * |
422 | * |
390 | * Used by GCM |
423 | * Used by GCM |
391 | * |
424 | * |
392 | * @var string|null |
425 | * @var string|null |
- | 426 | * @access private |
|
393 | */ |
427 | */ |
394 | private $decryptInvocationCounter; |
428 | private $decryptInvocationCounter; |
395 | 429 | ||
396 | /** |
430 | /** |
397 | * Fixed Part of Nonce |
431 | * Fixed Part of Nonce |
398 | * |
432 | * |
399 | * Used by GCM |
433 | * Used by GCM |
400 | * |
434 | * |
401 | * @var string|null |
435 | * @var string|null |
- | 436 | * @access private |
|
402 | */ |
437 | */ |
403 | private $decryptFixedPart; |
438 | private $decryptFixedPart; |
404 | 439 | ||
405 | /** |
440 | /** |
406 | * Server to Client Length Encryption Object |
441 | * Server to Client Length Encryption Object |
407 | * |
442 | * |
408 | * @see self::_get_binary_packet() |
443 | * @see self::_get_binary_packet() |
409 | * @var object |
444 | * @var object |
- | 445 | * @access private |
|
410 | */ |
446 | */ |
411 | private $lengthDecrypt = false; |
447 | private $lengthDecrypt = false; |
412 | 448 | ||
413 | /** |
449 | /** |
414 | * Client to Server Encryption Object |
450 | * Client to Server Encryption Object |
415 | * |
451 | * |
416 | * @see self::_send_binary_packet() |
452 | * @see self::_send_binary_packet() |
417 | * @var SymmetricKey|false |
453 | * @var SymmetricKey|false |
- | 454 | * @access private |
|
418 | */ |
455 | */ |
419 | private $encrypt = false; |
456 | private $encrypt = false; |
420 | 457 | ||
421 | /** |
458 | /** |
422 | * Encryption Algorithm Name |
459 | * Encryption Algorithm Name |
423 | * |
460 | * |
424 | * @var string|null |
461 | * @var string|null |
- | 462 | * @access private |
|
425 | */ |
463 | */ |
426 | private $encryptName; |
464 | private $encryptName; |
427 | 465 | ||
428 | /** |
466 | /** |
429 | * Encryption Invocation Counter |
467 | * Encryption Invocation Counter |
430 | * |
468 | * |
431 | * Used by GCM |
469 | * Used by GCM |
432 | * |
470 | * |
433 | * @var string|null |
471 | * @var string|null |
- | 472 | * @access private |
|
434 | */ |
473 | */ |
435 | private $encryptInvocationCounter; |
474 | private $encryptInvocationCounter; |
436 | 475 | ||
437 | /** |
476 | /** |
438 | * Fixed Part of Nonce |
477 | * Fixed Part of Nonce |
439 | * |
478 | * |
440 | * Used by GCM |
479 | * Used by GCM |
441 | * |
480 | * |
442 | * @var string|null |
481 | * @var string|null |
- | 482 | * @access private |
|
443 | */ |
483 | */ |
444 | private $encryptFixedPart; |
484 | private $encryptFixedPart; |
445 | 485 | ||
446 | /** |
486 | /** |
447 | * Client to Server Length Encryption Object |
487 | * Client to Server Length Encryption Object |
448 | * |
488 | * |
449 | * @see self::_send_binary_packet() |
489 | * @see self::_send_binary_packet() |
450 | * @var object |
490 | * @var object |
- | 491 | * @access private |
|
451 | */ |
492 | */ |
452 | private $lengthEncrypt = false; |
493 | private $lengthEncrypt = false; |
453 | 494 | ||
454 | /** |
495 | /** |
455 | * Client to Server HMAC Object |
496 | * Client to Server HMAC Object |
456 | * |
497 | * |
457 | * @see self::_send_binary_packet() |
498 | * @see self::_send_binary_packet() |
458 | * @var object |
499 | * @var object |
- | 500 | * @access private |
|
459 | */ |
501 | */ |
460 | private $hmac_create = false; |
502 | private $hmac_create = false; |
461 | 503 | ||
462 | /** |
504 | /** |
463 | * Client to Server HMAC Name |
505 | * Client to Server HMAC Name |
464 | * |
506 | * |
465 | * @var string|false |
507 | * @var string|false |
- | 508 | * @access private |
|
466 | */ |
509 | */ |
467 | private $hmac_create_name; |
510 | private $hmac_create_name; |
468 | 511 | ||
469 | /** |
512 | /** |
470 | * Client to Server ETM |
513 | * Client to Server ETM |
471 | * |
514 | * |
472 | * @var int|false |
515 | * @var int|false |
- | 516 | * @access private |
|
473 | */ |
517 | */ |
474 | private $hmac_create_etm; |
518 | private $hmac_create_etm; |
475 | 519 | ||
476 | /** |
520 | /** |
477 | * Server to Client HMAC Object |
521 | * Server to Client HMAC Object |
478 | * |
522 | * |
479 | * @see self::_get_binary_packet() |
523 | * @see self::_get_binary_packet() |
480 | * @var object |
524 | * @var object |
- | 525 | * @access private |
|
481 | */ |
526 | */ |
482 | private $hmac_check = false; |
527 | private $hmac_check = false; |
483 | 528 | ||
484 | /** |
529 | /** |
485 | * Server to Client HMAC Name |
530 | * Server to Client HMAC Name |
486 | * |
531 | * |
487 | * @var string|false |
532 | * @var string|false |
- | 533 | * @access private |
|
488 | */ |
534 | */ |
489 | private $hmac_check_name; |
535 | private $hmac_check_name; |
490 | 536 | ||
491 | /** |
537 | /** |
492 | * Server to Client ETM |
538 | * Server to Client ETM |
493 | * |
539 | * |
494 | * @var int|false |
540 | * @var int|false |
- | 541 | * @access private |
|
495 | */ |
542 | */ |
496 | private $hmac_check_etm; |
543 | private $hmac_check_etm; |
497 | 544 | ||
498 | /** |
545 | /** |
499 | * Size of server to client HMAC |
546 | * Size of server to client HMAC |
Line 502... | Line 549... | ||
502 | * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is |
549 | * For the client to server side, the HMAC object will make the HMAC as long as it needs to be. All we need to do is |
503 | * append it. |
550 | * append it. |
504 | * |
551 | * |
505 | * @see self::_get_binary_packet() |
552 | * @see self::_get_binary_packet() |
506 | * @var int |
553 | * @var int |
- | 554 | * @access private |
|
507 | */ |
555 | */ |
508 | private $hmac_size = false; |
556 | private $hmac_size = false; |
509 | 557 | ||
510 | /** |
558 | /** |
511 | * Server Public Host Key |
559 | * Server Public Host Key |
512 | * |
560 | * |
513 | * @see self::getServerPublicHostKey() |
561 | * @see self::getServerPublicHostKey() |
514 | * @var string |
562 | * @var string |
- | 563 | * @access private |
|
515 | */ |
564 | */ |
516 | private $server_public_host_key; |
565 | private $server_public_host_key; |
517 | 566 | ||
518 | /** |
567 | /** |
519 | * Session identifier |
568 | * Session identifier |
Line 524... | Line 573... | ||
524 | * |
573 | * |
525 | * -- http://tools.ietf.org/html/rfc4253#section-7.2 |
574 | * -- http://tools.ietf.org/html/rfc4253#section-7.2 |
526 | * |
575 | * |
527 | * @see self::_key_exchange() |
576 | * @see self::_key_exchange() |
528 | * @var string |
577 | * @var string |
- | 578 | * @access private |
|
529 | */ |
579 | */ |
530 | private $session_id = false; |
580 | private $session_id = false; |
531 | 581 | ||
532 | /** |
582 | /** |
533 | * Exchange hash |
583 | * Exchange hash |
534 | * |
584 | * |
535 | * The current exchange hash |
585 | * The current exchange hash |
536 | * |
586 | * |
537 | * @see self::_key_exchange() |
587 | * @see self::_key_exchange() |
538 | * @var string |
588 | * @var string |
- | 589 | * @access private |
|
539 | */ |
590 | */ |
540 | private $exchange_hash = false; |
591 | private $exchange_hash = false; |
541 | 592 | ||
542 | /** |
593 | /** |
- | 594 | * Message Numbers |
|
- | 595 | * |
|
- | 596 | * @see self::__construct() |
|
- | 597 | * @var array |
|
- | 598 | * @access private |
|
- | 599 | */ |
|
- | 600 | private $message_numbers = []; |
|
- | 601 | ||
- | 602 | /** |
|
- | 603 | * Disconnection Message 'reason codes' defined in RFC4253 |
|
- | 604 | * |
|
- | 605 | * @see self::__construct() |
|
- | 606 | * @var array |
|
- | 607 | * @access private |
|
- | 608 | */ |
|
- | 609 | private $disconnect_reasons = []; |
|
- | 610 | ||
- | 611 | /** |
|
- | 612 | * SSH_MSG_CHANNEL_OPEN_FAILURE 'reason codes', defined in RFC4254 |
|
- | 613 | * |
|
- | 614 | * @see self::__construct() |
|
- | 615 | * @var array |
|
- | 616 | * @access private |
|
- | 617 | */ |
|
- | 618 | private $channel_open_failure_reasons = []; |
|
- | 619 | ||
- | 620 | /** |
|
- | 621 | * Terminal Modes |
|
- | 622 | * |
|
- | 623 | * @link http://tools.ietf.org/html/rfc4254#section-8 |
|
- | 624 | * @see self::__construct() |
|
- | 625 | * @var array |
|
- | 626 | * @access private |
|
- | 627 | */ |
|
- | 628 | private $terminal_modes = []; |
|
- | 629 | ||
- | 630 | /** |
|
- | 631 | * SSH_MSG_CHANNEL_EXTENDED_DATA's data_type_codes |
|
- | 632 | * |
|
- | 633 | * @link http://tools.ietf.org/html/rfc4254#section-5.2 |
|
- | 634 | * @see self::__construct() |
|
- | 635 | * @var array |
|
- | 636 | * @access private |
|
- | 637 | */ |
|
- | 638 | private $channel_extended_data_type_codes = []; |
|
- | 639 | ||
- | 640 | /** |
|
543 | * Send Sequence Number |
641 | * Send Sequence Number |
544 | * |
642 | * |
545 | * See 'Section 6.4. Data Integrity' of rfc4253 for more info. |
643 | * See 'Section 6.4. Data Integrity' of rfc4253 for more info. |
546 | * |
644 | * |
547 | * @see self::_send_binary_packet() |
645 | * @see self::_send_binary_packet() |
548 | * @var int |
646 | * @var int |
- | 647 | * @access private |
|
549 | */ |
648 | */ |
550 | private $send_seq_no = 0; |
649 | private $send_seq_no = 0; |
551 | 650 | ||
552 | /** |
651 | /** |
553 | * Get Sequence Number |
652 | * Get Sequence Number |
554 | * |
653 | * |
555 | * See 'Section 6.4. Data Integrity' of rfc4253 for more info. |
654 | * See 'Section 6.4. Data Integrity' of rfc4253 for more info. |
556 | * |
655 | * |
557 | * @see self::_get_binary_packet() |
656 | * @see self::_get_binary_packet() |
558 | * @var int |
657 | * @var int |
- | 658 | * @access private |
|
559 | */ |
659 | */ |
560 | private $get_seq_no = 0; |
660 | private $get_seq_no = 0; |
561 | 661 | ||
562 | /** |
662 | /** |
563 | * Server Channels |
663 | * Server Channels |
Line 565... | Line 665... | ||
565 | * Maps client channels to server channels |
665 | * Maps client channels to server channels |
566 | * |
666 | * |
567 | * @see self::get_channel_packet() |
667 | * @see self::get_channel_packet() |
568 | * @see self::exec() |
668 | * @see self::exec() |
569 | * @var array |
669 | * @var array |
- | 670 | * @access private |
|
570 | */ |
671 | */ |
571 | protected $server_channels = []; |
672 | protected $server_channels = []; |
572 | 673 | ||
573 | /** |
674 | /** |
574 | * Channel Buffers |
675 | * Channel Buffers |
Line 577... | Line 678... | ||
577 | * be placed in a buffer |
678 | * be placed in a buffer |
578 | * |
679 | * |
579 | * @see self::get_channel_packet() |
680 | * @see self::get_channel_packet() |
580 | * @see self::exec() |
681 | * @see self::exec() |
581 | * @var array |
682 | * @var array |
- | 683 | * @access private |
|
582 | */ |
684 | */ |
583 | private $channel_buffers = []; |
685 | private $channel_buffers = []; |
584 | 686 | ||
585 | /** |
687 | /** |
586 | * Channel Status |
688 | * Channel Status |
587 | * |
689 | * |
588 | * Contains the type of the last sent message |
690 | * Contains the type of the last sent message |
589 | * |
691 | * |
590 | * @see self::get_channel_packet() |
692 | * @see self::get_channel_packet() |
591 | * @var array |
693 | * @var array |
- | 694 | * @access private |
|
592 | */ |
695 | */ |
593 | protected $channel_status = []; |
696 | protected $channel_status = []; |
594 | 697 | ||
595 | /** |
698 | /** |
596 | * Packet Size |
699 | * Packet Size |
597 | * |
700 | * |
598 | * Maximum packet size indexed by channel |
701 | * Maximum packet size indexed by channel |
599 | * |
702 | * |
600 | * @see self::send_channel_packet() |
703 | * @see self::send_channel_packet() |
601 | * @var array |
704 | * @var array |
- | 705 | * @access private |
|
602 | */ |
706 | */ |
603 | private $packet_size_client_to_server = []; |
707 | private $packet_size_client_to_server = []; |
604 | 708 | ||
605 | /** |
709 | /** |
606 | * Message Number Log |
710 | * Message Number Log |
607 | * |
711 | * |
608 | * @see self::getLog() |
712 | * @see self::getLog() |
609 | * @var array |
713 | * @var array |
- | 714 | * @access private |
|
610 | */ |
715 | */ |
611 | private $message_number_log = []; |
716 | private $message_number_log = []; |
612 | 717 | ||
613 | /** |
718 | /** |
614 | * Message Log |
719 | * Message Log |
615 | * |
720 | * |
616 | * @see self::getLog() |
721 | * @see self::getLog() |
617 | * @var array |
722 | * @var array |
- | 723 | * @access private |
|
618 | */ |
724 | */ |
619 | private $message_log = []; |
725 | private $message_log = []; |
620 | 726 | ||
621 | /** |
727 | /** |
622 | * The Window Size |
728 | * The Window Size |
Line 624... | Line 730... | ||
624 | * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) |
730 | * Bytes the other party can send before it must wait for the window to be adjusted (0x7FFFFFFF = 2GB) |
625 | * |
731 | * |
626 | * @var int |
732 | * @var int |
627 | * @see self::send_channel_packet() |
733 | * @see self::send_channel_packet() |
628 | * @see self::exec() |
734 | * @see self::exec() |
- | 735 | * @access private |
|
629 | */ |
736 | */ |
630 | protected $window_size = 0x7FFFFFFF; |
737 | protected $window_size = 0x7FFFFFFF; |
631 | 738 | ||
632 | /** |
739 | /** |
633 | * What we resize the window to |
740 | * What we resize the window to |
Line 637... | Line 744... | ||
637 | * we'll just do what PuTTY does |
744 | * we'll just do what PuTTY does |
638 | * |
745 | * |
639 | * @var int |
746 | * @var int |
640 | * @see self::_send_channel_packet() |
747 | * @see self::_send_channel_packet() |
641 | * @see self::exec() |
748 | * @see self::exec() |
- | 749 | * @access private |
|
642 | */ |
750 | */ |
643 | private $window_resize = 0x40000000; |
751 | private $window_resize = 0x40000000; |
644 | 752 | ||
645 | /** |
753 | /** |
646 | * Window size, server to client |
754 | * Window size, server to client |
647 | * |
755 | * |
648 | * Window size indexed by channel |
756 | * Window size indexed by channel |
649 | * |
757 | * |
650 | * @see self::send_channel_packet() |
758 | * @see self::send_channel_packet() |
651 | * @var array |
759 | * @var array |
- | 760 | * @access private |
|
652 | */ |
761 | */ |
653 | protected $window_size_server_to_client = []; |
762 | protected $window_size_server_to_client = []; |
654 | 763 | ||
655 | /** |
764 | /** |
656 | * Window size, client to server |
765 | * Window size, client to server |
657 | * |
766 | * |
658 | * Window size indexed by channel |
767 | * Window size indexed by channel |
659 | * |
768 | * |
660 | * @see self::get_channel_packet() |
769 | * @see self::get_channel_packet() |
661 | * @var array |
770 | * @var array |
- | 771 | * @access private |
|
662 | */ |
772 | */ |
663 | private $window_size_client_to_server = []; |
773 | private $window_size_client_to_server = []; |
664 | 774 | ||
665 | /** |
775 | /** |
666 | * Server signature |
776 | * Server signature |
667 | * |
777 | * |
668 | * Verified against $this->session_id |
778 | * Verified against $this->session_id |
669 | * |
779 | * |
670 | * @see self::getServerPublicHostKey() |
780 | * @see self::getServerPublicHostKey() |
671 | * @var string |
781 | * @var string |
- | 782 | * @access private |
|
672 | */ |
783 | */ |
673 | private $signature = ''; |
784 | private $signature = ''; |
674 | 785 | ||
675 | /** |
786 | /** |
676 | * Server signature format |
787 | * Server signature format |
677 | * |
788 | * |
678 | * ssh-rsa or ssh-dss. |
789 | * ssh-rsa or ssh-dss. |
679 | * |
790 | * |
680 | * @see self::getServerPublicHostKey() |
791 | * @see self::getServerPublicHostKey() |
681 | * @var string |
792 | * @var string |
- | 793 | * @access private |
|
682 | */ |
794 | */ |
683 | private $signature_format = ''; |
795 | private $signature_format = ''; |
684 | 796 | ||
685 | /** |
797 | /** |
686 | * Interactive Buffer |
798 | * Interactive Buffer |
687 | * |
799 | * |
688 | * @see self::read() |
800 | * @see self::read() |
689 | * @var string |
801 | * @var string |
- | 802 | * @access private |
|
690 | */ |
803 | */ |
691 | private $interactiveBuffer = ''; |
804 | private $interactiveBuffer = ''; |
692 | 805 | ||
693 | /** |
806 | /** |
694 | * Current log size |
807 | * Current log size |
Line 696... | Line 809... | ||
696 | * Should never exceed self::LOG_MAX_SIZE |
809 | * Should never exceed self::LOG_MAX_SIZE |
697 | * |
810 | * |
698 | * @see self::_send_binary_packet() |
811 | * @see self::_send_binary_packet() |
699 | * @see self::_get_binary_packet() |
812 | * @see self::_get_binary_packet() |
700 | * @var int |
813 | * @var int |
- | 814 | * @access private |
|
701 | */ |
815 | */ |
702 | private $log_size; |
816 | private $log_size; |
703 | 817 | ||
704 | /** |
818 | /** |
705 | * Timeout |
819 | * Timeout |
706 | * |
820 | * |
707 | * @see self::setTimeout() |
821 | * @see self::setTimeout() |
- | 822 | * @access private |
|
708 | */ |
823 | */ |
709 | protected $timeout; |
824 | protected $timeout; |
710 | 825 | ||
711 | /** |
826 | /** |
712 | * Current Timeout |
827 | * Current Timeout |
713 | * |
828 | * |
714 | * @see self::get_channel_packet() |
829 | * @see self::get_channel_packet() |
- | 830 | * @access private |
|
715 | */ |
831 | */ |
716 | protected $curTimeout; |
832 | protected $curTimeout; |
717 | 833 | ||
718 | /** |
834 | /** |
719 | * Keep Alive Interval |
835 | * Keep Alive Interval |
720 | * |
836 | * |
721 | * @see self::setKeepAlive() |
837 | * @see self::setKeepAlive() |
- | 838 | * @access private |
|
722 | */ |
839 | */ |
723 | private $keepAlive; |
840 | private $keepAlive; |
724 | 841 | ||
725 | /** |
842 | /** |
726 | * Real-time log file pointer |
843 | * Real-time log file pointer |
727 | * |
844 | * |
728 | * @see self::_append_log() |
845 | * @see self::_append_log() |
729 | * @var resource|closed-resource |
846 | * @var resource|closed-resource |
- | 847 | * @access private |
|
730 | */ |
848 | */ |
731 | private $realtime_log_file; |
849 | private $realtime_log_file; |
732 | 850 | ||
733 | /** |
851 | /** |
734 | * Real-time log file size |
852 | * Real-time log file size |
735 | * |
853 | * |
736 | * @see self::_append_log() |
854 | * @see self::_append_log() |
737 | * @var int |
855 | * @var int |
- | 856 | * @access private |
|
738 | */ |
857 | */ |
739 | private $realtime_log_size; |
858 | private $realtime_log_size; |
740 | 859 | ||
741 | /** |
860 | /** |
742 | * Has the signature been validated? |
861 | * Has the signature been validated? |
743 | * |
862 | * |
744 | * @see self::getServerPublicHostKey() |
863 | * @see self::getServerPublicHostKey() |
745 | * @var bool |
864 | * @var bool |
- | 865 | * @access private |
|
746 | */ |
866 | */ |
747 | private $signature_validated = false; |
867 | private $signature_validated = false; |
748 | 868 | ||
749 | /** |
869 | /** |
750 | * Real-time log file wrap boolean |
870 | * Real-time log file wrap boolean |
751 | * |
871 | * |
752 | * @see self::_append_log() |
872 | * @see self::_append_log() |
- | 873 | * @access private |
|
753 | */ |
874 | */ |
754 | private $realtime_log_wrap; |
875 | private $realtime_log_wrap; |
755 | 876 | ||
756 | /** |
877 | /** |
757 | * Flag to suppress stderr from output |
878 | * Flag to suppress stderr from output |
758 | * |
879 | * |
759 | * @see self::enableQuietMode() |
880 | * @see self::enableQuietMode() |
- | 881 | * @access private |
|
760 | */ |
882 | */ |
761 | private $quiet_mode = false; |
883 | private $quiet_mode = false; |
762 | 884 | ||
763 | /** |
885 | /** |
764 | * Time of first network activity |
886 | * Time of first network activity |
765 | * |
887 | * |
766 | * @var float |
888 | * @var float |
- | 889 | * @access private |
|
767 | */ |
890 | */ |
768 | private $last_packet; |
891 | private $last_packet; |
769 | 892 | ||
770 | /** |
893 | /** |
771 | * Exit status returned from ssh if any |
894 | * Exit status returned from ssh if any |
772 | * |
895 | * |
773 | * @var int |
896 | * @var int |
- | 897 | * @access private |
|
774 | */ |
898 | */ |
775 | private $exit_status; |
899 | private $exit_status; |
776 | 900 | ||
777 | /** |
901 | /** |
778 | * Flag to request a PTY when using exec() |
902 | * Flag to request a PTY when using exec() |
779 | * |
903 | * |
780 | * @var bool |
904 | * @var bool |
781 | * @see self::enablePTY() |
905 | * @see self::enablePTY() |
- | 906 | * @access private |
|
782 | */ |
907 | */ |
783 | private $request_pty = false; |
908 | private $request_pty = false; |
784 | 909 | ||
785 | /** |
910 | /** |
786 | * Flag set while exec() is running when using enablePTY() |
911 | * Flag set while exec() is running when using enablePTY() |
787 | * |
912 | * |
788 | * @var bool |
913 | * @var bool |
- | 914 | * @access private |
|
789 | */ |
915 | */ |
790 | private $in_request_pty_exec = false; |
916 | private $in_request_pty_exec = false; |
791 | 917 | ||
792 | /** |
918 | /** |
793 | * Flag set after startSubsystem() is called |
919 | * Flag set after startSubsystem() is called |
794 | * |
920 | * |
795 | * @var bool |
921 | * @var bool |
- | 922 | * @access private |
|
796 | */ |
923 | */ |
797 | private $in_subsystem; |
924 | private $in_subsystem; |
798 | 925 | ||
799 | /** |
926 | /** |
800 | * Contents of stdError |
927 | * Contents of stdError |
801 | * |
928 | * |
802 | * @var string |
929 | * @var string |
- | 930 | * @access private |
|
803 | */ |
931 | */ |
804 | private $stdErrorLog; |
932 | private $stdErrorLog; |
805 | 933 | ||
806 | /** |
934 | /** |
807 | * The Last Interactive Response |
935 | * The Last Interactive Response |
808 | * |
936 | * |
809 | * @see self::_keyboard_interactive_process() |
937 | * @see self::_keyboard_interactive_process() |
810 | * @var string |
938 | * @var string |
- | 939 | * @access private |
|
811 | */ |
940 | */ |
812 | private $last_interactive_response = ''; |
941 | private $last_interactive_response = ''; |
813 | 942 | ||
814 | /** |
943 | /** |
815 | * Keyboard Interactive Request / Responses |
944 | * Keyboard Interactive Request / Responses |
816 | * |
945 | * |
817 | * @see self::_keyboard_interactive_process() |
946 | * @see self::_keyboard_interactive_process() |
818 | * @var array |
947 | * @var array |
- | 948 | * @access private |
|
819 | */ |
949 | */ |
820 | private $keyboard_requests_responses = []; |
950 | private $keyboard_requests_responses = []; |
821 | 951 | ||
822 | /** |
952 | /** |
823 | * Banner Message |
953 | * Banner Message |
Line 826... | Line 956... | ||
826 | * authentication may be relevant for getting legal protection." |
956 | * authentication may be relevant for getting legal protection." |
827 | * |
957 | * |
828 | * @see self::_filter() |
958 | * @see self::_filter() |
829 | * @see self::getBannerMessage() |
959 | * @see self::getBannerMessage() |
830 | * @var string |
960 | * @var string |
- | 961 | * @access private |
|
831 | */ |
962 | */ |
832 | private $banner_message = ''; |
963 | private $banner_message = ''; |
833 | 964 | ||
834 | /** |
965 | /** |
835 | * Did read() timeout or return normally? |
966 | * Did read() timeout or return normally? |
836 | * |
967 | * |
837 | * @see self::isTimeout() |
968 | * @see self::isTimeout() |
838 | * @var bool |
969 | * @var bool |
- | 970 | * @access private |
|
839 | */ |
971 | */ |
840 | private $is_timeout = false; |
972 | private $is_timeout = false; |
841 | 973 | ||
842 | /** |
974 | /** |
843 | * Log Boundary |
975 | * Log Boundary |
844 | * |
976 | * |
845 | * @see self::_format_log() |
977 | * @see self::_format_log() |
846 | * @var string |
978 | * @var string |
- | 979 | * @access private |
|
847 | */ |
980 | */ |
848 | private $log_boundary = ':'; |
981 | private $log_boundary = ':'; |
849 | 982 | ||
850 | /** |
983 | /** |
851 | * Log Long Width |
984 | * Log Long Width |
852 | * |
985 | * |
853 | * @see self::_format_log() |
986 | * @see self::_format_log() |
854 | * @var int |
987 | * @var int |
- | 988 | * @access private |
|
855 | */ |
989 | */ |
856 | private $log_long_width = 65; |
990 | private $log_long_width = 65; |
857 | 991 | ||
858 | /** |
992 | /** |
859 | * Log Short Width |
993 | * Log Short Width |
860 | * |
994 | * |
861 | * @see self::_format_log() |
995 | * @see self::_format_log() |
862 | * @var int |
996 | * @var int |
- | 997 | * @access private |
|
863 | */ |
998 | */ |
864 | private $log_short_width = 16; |
999 | private $log_short_width = 16; |
865 | 1000 | ||
866 | /** |
1001 | /** |
867 | * Hostname |
1002 | * Hostname |
868 | * |
1003 | * |
869 | * @see self::__construct() |
1004 | * @see self::__construct() |
870 | * @see self::_connect() |
1005 | * @see self::_connect() |
871 | * @var string |
1006 | * @var string |
- | 1007 | * @access private |
|
872 | */ |
1008 | */ |
873 | private $host; |
1009 | private $host; |
874 | 1010 | ||
875 | /** |
1011 | /** |
876 | * Port Number |
1012 | * Port Number |
877 | * |
1013 | * |
878 | * @see self::__construct() |
1014 | * @see self::__construct() |
879 | * @see self::_connect() |
1015 | * @see self::_connect() |
880 | * @var int |
1016 | * @var int |
- | 1017 | * @access private |
|
881 | */ |
1018 | */ |
882 | private $port; |
1019 | private $port; |
883 | 1020 | ||
884 | /** |
1021 | /** |
885 | * Number of columns for terminal window size |
1022 | * Number of columns for terminal window size |
886 | * |
1023 | * |
887 | * @see self::getWindowColumns() |
1024 | * @see self::getWindowColumns() |
888 | * @see self::setWindowColumns() |
1025 | * @see self::setWindowColumns() |
889 | * @see self::setWindowSize() |
1026 | * @see self::setWindowSize() |
890 | * @var int |
1027 | * @var int |
- | 1028 | * @access private |
|
891 | */ |
1029 | */ |
892 | private $windowColumns = 80; |
1030 | private $windowColumns = 80; |
893 | 1031 | ||
894 | /** |
1032 | /** |
895 | * Number of columns for terminal window size |
1033 | * Number of columns for terminal window size |
896 | * |
1034 | * |
897 | * @see self::getWindowRows() |
1035 | * @see self::getWindowRows() |
898 | * @see self::setWindowRows() |
1036 | * @see self::setWindowRows() |
899 | * @see self::setWindowSize() |
1037 | * @see self::setWindowSize() |
900 | * @var int |
1038 | * @var int |
- | 1039 | * @access private |
|
901 | */ |
1040 | */ |
902 | private $windowRows = 24; |
1041 | private $windowRows = 24; |
903 | 1042 | ||
904 | /** |
1043 | /** |
905 | * Crypto Engine |
1044 | * Crypto Engine |
906 | * |
1045 | * |
907 | * @see self::setCryptoEngine() |
1046 | * @see self::setCryptoEngine() |
908 | * @see self::_key_exchange() |
1047 | * @see self::_key_exchange() |
909 | * @var int |
1048 | * @var int |
- | 1049 | * @access private |
|
910 | */ |
1050 | */ |
911 | private static $crypto_engine = false; |
1051 | private static $crypto_engine = false; |
912 | 1052 | ||
913 | /** |
1053 | /** |
914 | * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario |
1054 | * A System_SSH_Agent for use in the SSH2 Agent Forwarding scenario |
915 | * |
1055 | * |
916 | * @var Agent |
1056 | * @var Agent |
- | 1057 | * @access private |
|
917 | */ |
1058 | */ |
918 | private $agent; |
1059 | private $agent; |
919 | 1060 | ||
920 | /** |
1061 | /** |
921 | * Connection storage to replicates ssh2 extension functionality: |
1062 | * Connection storage to replicates ssh2 extension functionality: |
Line 927... | Line 1068... | ||
927 | 1068 | ||
928 | /** |
1069 | /** |
929 | * Send the identification string first? |
1070 | * Send the identification string first? |
930 | * |
1071 | * |
931 | * @var bool |
1072 | * @var bool |
- | 1073 | * @access private |
|
932 | */ |
1074 | */ |
933 | private $send_id_string_first = true; |
1075 | private $send_id_string_first = true; |
934 | 1076 | ||
935 | /** |
1077 | /** |
936 | * Send the key exchange initiation packet first? |
1078 | * Send the key exchange initiation packet first? |
937 | * |
1079 | * |
938 | * @var bool |
1080 | * @var bool |
- | 1081 | * @access private |
|
939 | */ |
1082 | */ |
940 | private $send_kex_first = true; |
1083 | private $send_kex_first = true; |
941 | 1084 | ||
942 | /** |
1085 | /** |
943 | * Some versions of OpenSSH incorrectly calculate the key size |
1086 | * Some versions of OpenSSH incorrectly calculate the key size |
944 | * |
1087 | * |
945 | * @var bool |
1088 | * @var bool |
- | 1089 | * @access private |
|
946 | */ |
1090 | */ |
947 | private $bad_key_size_fix = false; |
1091 | private $bad_key_size_fix = false; |
948 | 1092 | ||
949 | /** |
1093 | /** |
950 | * Should we try to re-connect to re-establish keys? |
1094 | * Should we try to re-connect to re-establish keys? |
951 | * |
1095 | * |
952 | * @var bool |
1096 | * @var bool |
- | 1097 | * @access private |
|
953 | */ |
1098 | */ |
954 | private $retry_connect = false; |
1099 | private $retry_connect = false; |
955 | 1100 | ||
956 | /** |
1101 | /** |
957 | * Binary Packet Buffer |
1102 | * Binary Packet Buffer |
958 | * |
1103 | * |
959 | * @var string|false |
1104 | * @var string|false |
- | 1105 | * @access private |
|
960 | */ |
1106 | */ |
961 | private $binary_packet_buffer = false; |
1107 | private $binary_packet_buffer = false; |
962 | 1108 | ||
963 | /** |
1109 | /** |
964 | * Preferred Signature Format |
1110 | * Preferred Signature Format |
965 | * |
1111 | * |
966 | * @var string|false |
1112 | * @var string|false |
- | 1113 | * @access private |
|
967 | */ |
1114 | */ |
968 | protected $preferred_signature_format = false; |
1115 | protected $preferred_signature_format = false; |
969 | 1116 | ||
970 | /** |
1117 | /** |
971 | * Authentication Credentials |
1118 | * Authentication Credentials |
972 | * |
1119 | * |
973 | * @var array |
1120 | * @var array |
- | 1121 | * @access private |
|
974 | */ |
1122 | */ |
975 | protected $auth = []; |
1123 | protected $auth = []; |
976 | 1124 | ||
977 | /** |
1125 | /** |
978 | * Terminal |
1126 | * Terminal |
979 | * |
1127 | * |
980 | * @var string |
1128 | * @var string |
- | 1129 | * @access private |
|
981 | */ |
1130 | */ |
982 | private $term = 'vt100'; |
1131 | private $term = 'vt100'; |
983 | 1132 | ||
984 | /** |
1133 | /** |
985 | * The authentication methods that may productively continue authentication. |
1134 | * The authentication methods that may productively continue authentication. |
986 | * |
1135 | * |
987 | * @see https://tools.ietf.org/html/rfc4252#section-5.1 |
1136 | * @see https://tools.ietf.org/html/rfc4252#section-5.1 |
988 | * @var array|null |
1137 | * @var array|null |
- | 1138 | * @access private |
|
989 | */ |
1139 | */ |
990 | private $auth_methods_to_continue = null; |
1140 | private $auth_methods_to_continue = null; |
991 | 1141 | ||
992 | /** |
1142 | /** |
993 | * Compression method |
1143 | * Compression method |
994 | * |
1144 | * |
995 | * @var int |
1145 | * @var int |
- | 1146 | * @access private |
|
996 | */ |
1147 | */ |
997 | private $compress = self::NET_SSH2_COMPRESSION_NONE; |
1148 | private $compress = self::NET_SSH2_COMPRESSION_NONE; |
998 | 1149 | ||
999 | /** |
1150 | /** |
1000 | * Decompression method |
1151 | * Decompression method |
1001 | * |
1152 | * |
1002 | * @var int |
1153 | * @var int |
- | 1154 | * @access private |
|
1003 | */ |
1155 | */ |
1004 | private $decompress = self::NET_SSH2_COMPRESSION_NONE; |
1156 | private $decompress = self::NET_SSH2_COMPRESSION_NONE; |
1005 | 1157 | ||
1006 | /** |
1158 | /** |
1007 | * Compression context |
1159 | * Compression context |
1008 | * |
1160 | * |
1009 | * @var resource|false|null |
1161 | * @var resource|false|null |
- | 1162 | * @access private |
|
1010 | */ |
1163 | */ |
1011 | private $compress_context; |
1164 | private $compress_context; |
1012 | 1165 | ||
1013 | /** |
1166 | /** |
1014 | * Decompression context |
1167 | * Decompression context |
1015 | * |
1168 | * |
1016 | * @var resource|object |
1169 | * @var resource|object |
- | 1170 | * @access private |
|
1017 | */ |
1171 | */ |
1018 | private $decompress_context; |
1172 | private $decompress_context; |
1019 | 1173 | ||
1020 | /** |
1174 | /** |
1021 | * Regenerate Compression Context |
1175 | * Regenerate Compression Context |
1022 | * |
1176 | * |
1023 | * @var bool |
1177 | * @var bool |
- | 1178 | * @access private |
|
1024 | */ |
1179 | */ |
1025 | private $regenerate_compression_context = false; |
1180 | private $regenerate_compression_context = false; |
1026 | 1181 | ||
1027 | /** |
1182 | /** |
1028 | * Regenerate Decompression Context |
1183 | * Regenerate Decompression Context |
1029 | * |
1184 | * |
1030 | * @var bool |
1185 | * @var bool |
- | 1186 | * @access private |
|
1031 | */ |
1187 | */ |
1032 | private $regenerate_decompression_context = false; |
1188 | private $regenerate_decompression_context = false; |
1033 | 1189 | ||
1034 | /** |
1190 | /** |
1035 | * Smart multi-factor authentication flag |
1191 | * Smart multi-factor authentication flag |
1036 | * |
1192 | * |
1037 | * @var bool |
1193 | * @var bool |
- | 1194 | * @access private |
|
1038 | */ |
1195 | */ |
1039 | private $smartMFA = true; |
1196 | private $smartMFA = true; |
1040 | 1197 | ||
1041 | /** |
1198 | /** |
1042 | * Default Constructor. |
1199 | * Default Constructor. |
Line 1045... | Line 1202... | ||
1045 | * |
1202 | * |
1046 | * @param mixed $host |
1203 | * @param mixed $host |
1047 | * @param int $port |
1204 | * @param int $port |
1048 | * @param int $timeout |
1205 | * @param int $timeout |
1049 | * @see self::login() |
1206 | * @see self::login() |
- | 1207 | * @access public |
|
1050 | */ |
1208 | */ |
1051 | public function __construct($host, $port = 22, $timeout = 10) |
1209 | public function __construct($host, $port = 22, $timeout = 10) |
1052 | { |
1210 | { |
- | 1211 | $this->message_numbers = [ |
|
- | 1212 | 1 => 'NET_SSH2_MSG_DISCONNECT', |
|
- | 1213 | 2 => 'NET_SSH2_MSG_IGNORE', |
|
- | 1214 | 3 => 'NET_SSH2_MSG_UNIMPLEMENTED', |
|
- | 1215 | 4 => 'NET_SSH2_MSG_DEBUG', |
|
- | 1216 | 5 => 'NET_SSH2_MSG_SERVICE_REQUEST', |
|
- | 1217 | 6 => 'NET_SSH2_MSG_SERVICE_ACCEPT', |
|
- | 1218 | 20 => 'NET_SSH2_MSG_KEXINIT', |
|
- | 1219 | 21 => 'NET_SSH2_MSG_NEWKEYS', |
|
- | 1220 | 30 => 'NET_SSH2_MSG_KEXDH_INIT', |
|
- | 1221 | 31 => 'NET_SSH2_MSG_KEXDH_REPLY', |
|
- | 1222 | 50 => 'NET_SSH2_MSG_USERAUTH_REQUEST', |
|
- | 1223 | 51 => 'NET_SSH2_MSG_USERAUTH_FAILURE', |
|
- | 1224 | 52 => 'NET_SSH2_MSG_USERAUTH_SUCCESS', |
|
- | 1225 | 53 => 'NET_SSH2_MSG_USERAUTH_BANNER', |
|
- | 1226 | ||
- | 1227 | 80 => 'NET_SSH2_MSG_GLOBAL_REQUEST', |
|
- | 1228 | 81 => 'NET_SSH2_MSG_REQUEST_SUCCESS', |
|
- | 1229 | 82 => 'NET_SSH2_MSG_REQUEST_FAILURE', |
|
- | 1230 | 90 => 'NET_SSH2_MSG_CHANNEL_OPEN', |
|
- | 1231 | 91 => 'NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION', |
|
- | 1232 | 92 => 'NET_SSH2_MSG_CHANNEL_OPEN_FAILURE', |
|
- | 1233 | 93 => 'NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST', |
|
- | 1234 | 94 => 'NET_SSH2_MSG_CHANNEL_DATA', |
|
- | 1235 | 95 => 'NET_SSH2_MSG_CHANNEL_EXTENDED_DATA', |
|
- | 1236 | 96 => 'NET_SSH2_MSG_CHANNEL_EOF', |
|
- | 1237 | 97 => 'NET_SSH2_MSG_CHANNEL_CLOSE', |
|
- | 1238 | 98 => 'NET_SSH2_MSG_CHANNEL_REQUEST', |
|
- | 1239 | 99 => 'NET_SSH2_MSG_CHANNEL_SUCCESS', |
|
- | 1240 | 100 => 'NET_SSH2_MSG_CHANNEL_FAILURE' |
|
- | 1241 | ]; |
|
- | 1242 | $this->disconnect_reasons = [ |
|
- | 1243 | 1 => 'NET_SSH2_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT', |
|
- | 1244 | 2 => 'NET_SSH2_DISCONNECT_PROTOCOL_ERROR', |
|
- | 1245 | 3 => 'NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED', |
|
- | 1246 | 4 => 'NET_SSH2_DISCONNECT_RESERVED', |
|
- | 1247 | 5 => 'NET_SSH2_DISCONNECT_MAC_ERROR', |
|
- | 1248 | 6 => 'NET_SSH2_DISCONNECT_COMPRESSION_ERROR', |
|
- | 1249 | 7 => 'NET_SSH2_DISCONNECT_SERVICE_NOT_AVAILABLE', |
|
- | 1250 | 8 => 'NET_SSH2_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED', |
|
- | 1251 | 9 => 'NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE', |
|
- | 1252 | 10 => 'NET_SSH2_DISCONNECT_CONNECTION_LOST', |
|
- | 1253 | 11 => 'NET_SSH2_DISCONNECT_BY_APPLICATION', |
|
- | 1254 | 12 => 'NET_SSH2_DISCONNECT_TOO_MANY_CONNECTIONS', |
|
- | 1255 | 13 => 'NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER', |
|
- | 1256 | 14 => 'NET_SSH2_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE', |
|
- | 1257 | 15 => 'NET_SSH2_DISCONNECT_ILLEGAL_USER_NAME' |
|
- | 1258 | ]; |
|
- | 1259 | $this->channel_open_failure_reasons = [ |
|
- | 1260 | 1 => 'NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED' |
|
- | 1261 | ]; |
|
- | 1262 | $this->terminal_modes = [ |
|
- | 1263 | 0 => 'NET_SSH2_TTY_OP_END' |
|
- | 1264 | ]; |
|
- | 1265 | $this->channel_extended_data_type_codes = [ |
|
- | 1266 | 1 => 'NET_SSH2_EXTENDED_DATA_STDERR' |
|
- | 1267 | ]; |
|
- | 1268 | ||
- | 1269 | $this->define_array( |
|
- | 1270 | $this->message_numbers, |
|
- | 1271 | $this->disconnect_reasons, |
|
- | 1272 | $this->channel_open_failure_reasons, |
|
- | 1273 | $this->terminal_modes, |
|
- | 1274 | $this->channel_extended_data_type_codes, |
|
- | 1275 | [60 => 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'], |
|
- | 1276 | [60 => 'NET_SSH2_MSG_USERAUTH_PK_OK'], |
|
- | 1277 | [60 => 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST', |
|
- | 1278 | 61 => 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'], |
|
- | 1279 | // RFC 4419 - diffie-hellman-group-exchange-sha{1,256} |
|
- | 1280 | [30 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST_OLD', |
|
- | 1281 | 31 => 'NET_SSH2_MSG_KEXDH_GEX_GROUP', |
|
- | 1282 | 32 => 'NET_SSH2_MSG_KEXDH_GEX_INIT', |
|
- | 1283 | 33 => 'NET_SSH2_MSG_KEXDH_GEX_REPLY', |
|
- | 1284 | 34 => 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'], |
|
- | 1285 | // RFC 5656 - Elliptic Curves (for curve25519-sha256@libssh.org) |
|
- | 1286 | [30 => 'NET_SSH2_MSG_KEX_ECDH_INIT', |
|
- | 1287 | 31 => 'NET_SSH2_MSG_KEX_ECDH_REPLY'] |
|
- | 1288 | ); |
|
- | 1289 | ||
1053 | /** |
1290 | /** |
1054 | * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508 |
1291 | * Typehint is required due to a bug in Psalm: https://github.com/vimeo/psalm/issues/7508 |
1055 | * @var \WeakReference<SSH2>|SSH2 |
1292 | * @var \WeakReference<SSH2>|SSH2 |
1056 | */ |
1293 | */ |
1057 | self::$connections[$this->getResourceId()] = class_exists('WeakReference') |
1294 | self::$connections[$this->getResourceId()] = class_exists('WeakReference') |
Line 1075... | Line 1312... | ||
1075 | * |
1312 | * |
1076 | * Possible $engine values: |
1313 | * Possible $engine values: |
1077 | * OpenSSL, mcrypt, Eval, PHP |
1314 | * OpenSSL, mcrypt, Eval, PHP |
1078 | * |
1315 | * |
1079 | * @param int $engine |
1316 | * @param int $engine |
- | 1317 | * @access public |
|
1080 | */ |
1318 | */ |
1081 | public static function setCryptoEngine($engine) |
1319 | public static function setCryptoEngine($engine) |
1082 | { |
1320 | { |
1083 | self::$crypto_engine = $engine; |
1321 | self::$crypto_engine = $engine; |
1084 | } |
1322 | } |
Line 1088... | Line 1326... | ||
1088 | * |
1326 | * |
1089 | * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, |
1327 | * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, |
1090 | * both sides MUST send an identification string". It does not say which side sends it first. In |
1328 | * both sides MUST send an identification string". It does not say which side sends it first. In |
1091 | * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1329 | * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1092 | * |
1330 | * |
- | 1331 | * @access public |
|
1093 | */ |
1332 | */ |
1094 | public function sendIdentificationStringFirst() |
1333 | public function sendIdentificationStringFirst() |
1095 | { |
1334 | { |
1096 | $this->send_id_string_first = true; |
1335 | $this->send_id_string_first = true; |
1097 | } |
1336 | } |
Line 1101... | Line 1340... | ||
1101 | * |
1340 | * |
1102 | * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, |
1341 | * https://tools.ietf.org/html/rfc4253#section-4.2 says "when the connection has been established, |
1103 | * both sides MUST send an identification string". It does not say which side sends it first. In |
1342 | * both sides MUST send an identification string". It does not say which side sends it first. In |
1104 | * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1343 | * theory it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1105 | * |
1344 | * |
- | 1345 | * @access public |
|
1106 | */ |
1346 | */ |
1107 | public function sendIdentificationStringLast() |
1347 | public function sendIdentificationStringLast() |
1108 | { |
1348 | { |
1109 | $this->send_id_string_first = false; |
1349 | $this->send_id_string_first = false; |
1110 | } |
1350 | } |
Line 1114... | Line 1354... | ||
1114 | * |
1354 | * |
1115 | * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending |
1355 | * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending |
1116 | * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory |
1356 | * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory |
1117 | * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1357 | * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1118 | * |
1358 | * |
- | 1359 | * @access public |
|
1119 | */ |
1360 | */ |
1120 | public function sendKEXINITFirst() |
1361 | public function sendKEXINITFirst() |
1121 | { |
1362 | { |
1122 | $this->send_kex_first = true; |
1363 | $this->send_kex_first = true; |
1123 | } |
1364 | } |
Line 1127... | Line 1368... | ||
1127 | * |
1368 | * |
1128 | * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending |
1369 | * https://tools.ietf.org/html/rfc4253#section-7.1 says "key exchange begins by each sending |
1129 | * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory |
1370 | * sending the [SSH_MSG_KEXINIT] packet". It does not say which side sends it first. In theory |
1130 | * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1371 | * it shouldn't matter but it is a fact of life that some SSH servers are simply buggy |
1131 | * |
1372 | * |
- | 1373 | * @access public |
|
1132 | */ |
1374 | */ |
1133 | public function sendKEXINITLast() |
1375 | public function sendKEXINITLast() |
1134 | { |
1376 | { |
1135 | $this->send_kex_first = false; |
1377 | $this->send_kex_first = false; |
1136 | } |
1378 | } |
Line 1138... | Line 1380... | ||
1138 | /** |
1380 | /** |
1139 | * Connect to an SSHv2 server |
1381 | * Connect to an SSHv2 server |
1140 | * |
1382 | * |
1141 | * @throws \UnexpectedValueException on receipt of unexpected packets |
1383 | * @throws \UnexpectedValueException on receipt of unexpected packets |
1142 | * @throws \RuntimeException on other errors |
1384 | * @throws \RuntimeException on other errors |
- | 1385 | * @access private |
|
1143 | */ |
1386 | */ |
1144 | private function connect() |
1387 | private function connect() |
1145 | { |
1388 | { |
1146 | if ($this->bitmap & self::MASK_CONSTRUCTOR) { |
1389 | if ($this->bitmap & self::MASK_CONSTRUCTOR) { |
1147 | return; |
1390 | return; |
Line 1259... | Line 1502... | ||
1259 | } |
1502 | } |
1260 | 1503 | ||
1261 | if (!$this->send_kex_first) { |
1504 | if (!$this->send_kex_first) { |
1262 | $response = $this->get_binary_packet(); |
1505 | $response = $this->get_binary_packet(); |
1263 | 1506 | ||
1264 | if (is_bool($response) || !strlen($response) || ord($response[0]) != MessageType::KEXINIT) { |
1507 | if (is_bool($response) || !strlen($response) || ord($response[0]) != NET_SSH2_MSG_KEXINIT) { |
1265 | $this->bitmap = 0; |
1508 | $this->bitmap = 0; |
1266 | throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); |
1509 | throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); |
1267 | } |
1510 | } |
1268 | 1511 | ||
1269 | $this->key_exchange($response); |
1512 | $this->key_exchange($response); |
Line 1281... | Line 1524... | ||
1281 | /** |
1524 | /** |
1282 | * Generates the SSH identifier |
1525 | * Generates the SSH identifier |
1283 | * |
1526 | * |
1284 | * You should overwrite this method in your own class if you want to use another identifier |
1527 | * You should overwrite this method in your own class if you want to use another identifier |
1285 | * |
1528 | * |
- | 1529 | * @access protected |
|
1286 | * @return string |
1530 | * @return string |
1287 | */ |
1531 | */ |
1288 | private function generate_identifier() |
1532 | private function generate_identifier() |
1289 | { |
1533 | { |
1290 | $identifier = 'SSH-2.0-phpseclib_3.0'; |
1534 | $identifier = 'SSH-2.0-phpseclib_3.0'; |
Line 1319... | Line 1563... | ||
1319 | * @return bool |
1563 | * @return bool |
1320 | * @param string|bool $kexinit_payload_server optional |
1564 | * @param string|bool $kexinit_payload_server optional |
1321 | * @throws \UnexpectedValueException on receipt of unexpected packets |
1565 | * @throws \UnexpectedValueException on receipt of unexpected packets |
1322 | * @throws \RuntimeException on other errors |
1566 | * @throws \RuntimeException on other errors |
1323 | * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible |
1567 | * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when none of the algorithms phpseclib has loaded are compatible |
- | 1568 | * @access private |
|
1324 | */ |
1569 | */ |
1325 | private function key_exchange($kexinit_payload_server = false) |
1570 | private function key_exchange($kexinit_payload_server = false) |
1326 | { |
1571 | { |
1327 | $preferred = $this->preferred; |
1572 | $preferred = $this->preferred; |
1328 | $send_kex = true; |
1573 | $send_kex = true; |
Line 1370... | Line 1615... | ||
1370 | } |
1615 | } |
1371 | } |
1616 | } |
1372 | 1617 | ||
1373 | $client_cookie = Random::string(16); |
1618 | $client_cookie = Random::string(16); |
1374 | 1619 | ||
1375 | $kexinit_payload_client = pack('Ca*', MessageType::KEXINIT, $client_cookie); |
1620 | $kexinit_payload_client = pack('Ca*', NET_SSH2_MSG_KEXINIT, $client_cookie); |
1376 | $kexinit_payload_client .= Strings::packSSH2( |
1621 | $kexinit_payload_client .= Strings::packSSH2( |
1377 | 'L10bN', |
1622 | 'L10bN', |
1378 | $kex_algorithms, |
1623 | $kex_algorithms, |
1379 | $server_host_key_algorithms, |
1624 | $server_host_key_algorithms, |
1380 | $c2s_encryption_algorithms, |
1625 | $c2s_encryption_algorithms, |
Line 1395... | Line 1640... | ||
1395 | $kexinit_payload_server = $this->get_binary_packet(); |
1640 | $kexinit_payload_server = $this->get_binary_packet(); |
1396 | 1641 | ||
1397 | if ( |
1642 | if ( |
1398 | is_bool($kexinit_payload_server) |
1643 | is_bool($kexinit_payload_server) |
1399 | || !strlen($kexinit_payload_server) |
1644 | || !strlen($kexinit_payload_server) |
1400 | || ord($kexinit_payload_server[0]) != MessageType::KEXINIT |
1645 | || ord($kexinit_payload_server[0]) != NET_SSH2_MSG_KEXINIT |
1401 | ) { |
1646 | ) { |
1402 | $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR); |
1647 | $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); |
1403 | throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); |
1648 | throw new \UnexpectedValueException('Expected SSH_MSG_KEXINIT'); |
1404 | } |
1649 | } |
1405 | 1650 | ||
1406 | $send_kex = false; |
1651 | $send_kex = false; |
1407 | } |
1652 | } |
Line 1433... | Line 1678... | ||
1433 | // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the |
1678 | // we don't initialize any crypto-objects, yet - we do that, later. for now, we need the lengths to make the |
1434 | // diffie-hellman key exchange as fast as possible |
1679 | // diffie-hellman key exchange as fast as possible |
1435 | $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client); |
1680 | $decrypt = self::array_intersect_first($s2c_encryption_algorithms, $this->encryption_algorithms_server_to_client); |
1436 | $decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt); |
1681 | $decryptKeyLength = $this->encryption_algorithm_to_key_size($decrypt); |
1437 | if ($decryptKeyLength === null) { |
1682 | if ($decryptKeyLength === null) { |
1438 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1683 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1439 | throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); |
1684 | throw new NoSupportedAlgorithmsException('No compatible server to client encryption algorithms found'); |
1440 | } |
1685 | } |
1441 | 1686 | ||
1442 | $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server); |
1687 | $encrypt = self::array_intersect_first($c2s_encryption_algorithms, $this->encryption_algorithms_client_to_server); |
1443 | $encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt); |
1688 | $encryptKeyLength = $this->encryption_algorithm_to_key_size($encrypt); |
1444 | if ($encryptKeyLength === null) { |
1689 | if ($encryptKeyLength === null) { |
1445 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1690 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1446 | throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); |
1691 | throw new NoSupportedAlgorithmsException('No compatible client to server encryption algorithms found'); |
1447 | } |
1692 | } |
1448 | 1693 | ||
1449 | // through diffie-hellman key exchange a symmetric key is obtained |
1694 | // through diffie-hellman key exchange a symmetric key is obtained |
1450 | $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms); |
1695 | $this->kex_algorithm = self::array_intersect_first($kex_algorithms, $this->kex_algorithms); |
1451 | if ($this->kex_algorithm === false) { |
1696 | if ($this->kex_algorithm === false) { |
1452 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1697 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1453 | throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); |
1698 | throw new NoSupportedAlgorithmsException('No compatible key exchange algorithms found'); |
1454 | } |
1699 | } |
1455 | 1700 | ||
1456 | $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); |
1701 | $server_host_key_algorithm = self::array_intersect_first($server_host_key_algorithms, $this->server_host_key_algorithms); |
1457 | if ($server_host_key_algorithm === false) { |
1702 | if ($server_host_key_algorithm === false) { |
1458 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1703 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1459 | throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found'); |
1704 | throw new NoSupportedAlgorithmsException('No compatible server host key algorithms found'); |
1460 | } |
1705 | } |
1461 | 1706 | ||
1462 | $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); |
1707 | $mac_algorithm_out = self::array_intersect_first($c2s_mac_algorithms, $this->mac_algorithms_client_to_server); |
1463 | if ($mac_algorithm_out === false) { |
1708 | if ($mac_algorithm_out === false) { |
1464 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1709 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1465 | throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); |
1710 | throw new NoSupportedAlgorithmsException('No compatible client to server message authentication algorithms found'); |
1466 | } |
1711 | } |
1467 | 1712 | ||
1468 | $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); |
1713 | $mac_algorithm_in = self::array_intersect_first($s2c_mac_algorithms, $this->mac_algorithms_server_to_client); |
1469 | if ($mac_algorithm_in === false) { |
1714 | if ($mac_algorithm_in === false) { |
1470 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1715 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1471 | throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); |
1716 | throw new NoSupportedAlgorithmsException('No compatible server to client message authentication algorithms found'); |
1472 | } |
1717 | } |
1473 | 1718 | ||
1474 | $compression_map = [ |
1719 | $compression_map = [ |
1475 | 'none' => self::NET_SSH2_COMPRESSION_NONE, |
1720 | 'none' => self::NET_SSH2_COMPRESSION_NONE, |
Line 1477... | Line 1722... | ||
1477 | 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH |
1722 | 'zlib@openssh.com' => self::NET_SSH2_COMPRESSION_ZLIB_AT_OPENSSH |
1478 | ]; |
1723 | ]; |
1479 | 1724 | ||
1480 | $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); |
1725 | $compression_algorithm_in = self::array_intersect_first($s2c_compression_algorithms, $this->compression_algorithms_server_to_client); |
1481 | if ($compression_algorithm_in === false) { |
1726 | if ($compression_algorithm_in === false) { |
1482 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1727 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1483 | throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); |
1728 | throw new NoSupportedAlgorithmsException('No compatible server to client compression algorithms found'); |
1484 | } |
1729 | } |
1485 | $this->decompress = $compression_map[$compression_algorithm_in]; |
1730 | $this->decompress = $compression_map[$compression_algorithm_in]; |
1486 | 1731 | ||
1487 | $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); |
1732 | $compression_algorithm_out = self::array_intersect_first($c2s_compression_algorithms, $this->compression_algorithms_client_to_server); |
1488 | if ($compression_algorithm_out === false) { |
1733 | if ($compression_algorithm_out === false) { |
1489 | $this->disconnect_helper(DisconnectReason::KEY_EXCHANGE_FAILED); |
1734 | $this->disconnect_helper(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
1490 | throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); |
1735 | throw new NoSupportedAlgorithmsException('No compatible client to server compression algorithms found'); |
1491 | } |
1736 | } |
1492 | $this->compress = $compression_map[$compression_algorithm_out]; |
1737 | $this->compress = $compression_map[$compression_algorithm_out]; |
1493 | 1738 | ||
1494 | switch ($this->kex_algorithm) { |
1739 | switch ($this->kex_algorithm) { |
Line 1521... | Line 1766... | ||
1521 | $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ? |
1766 | $curve = strpos($this->kex_algorithm, 'curve25519-sha256') === 0 ? |
1522 | 'Curve25519' : |
1767 | 'Curve25519' : |
1523 | substr($this->kex_algorithm, 10); |
1768 | substr($this->kex_algorithm, 10); |
1524 | $ourPrivate = EC::createKey($curve); |
1769 | $ourPrivate = EC::createKey($curve); |
1525 | $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates(); |
1770 | $ourPublicBytes = $ourPrivate->getPublicKey()->getEncodedCoordinates(); |
1526 | $clientKexInitMessage = MessageTypeExtra::KEX_ECDH_INIT; |
1771 | $clientKexInitMessage = 'NET_SSH2_MSG_KEX_ECDH_INIT'; |
1527 | $serverKexReplyMessage = MessageTypeExtra::KEX_ECDH_REPLY; |
1772 | $serverKexReplyMessage = 'NET_SSH2_MSG_KEX_ECDH_REPLY'; |
1528 | } else { |
1773 | } else { |
1529 | if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) { |
1774 | if (strpos($this->kex_algorithm, 'diffie-hellman-group-exchange') === 0) { |
1530 | $dh_group_sizes_packed = pack( |
1775 | $dh_group_sizes_packed = pack( |
1531 | 'NNN', |
1776 | 'NNN', |
1532 | $this->kex_dh_group_size_min, |
1777 | $this->kex_dh_group_size_min, |
1533 | $this->kex_dh_group_size_preferred, |
1778 | $this->kex_dh_group_size_preferred, |
1534 | $this->kex_dh_group_size_max |
1779 | $this->kex_dh_group_size_max |
1535 | ); |
1780 | ); |
1536 | $packet = pack( |
1781 | $packet = pack( |
1537 | 'Ca*', |
1782 | 'Ca*', |
1538 | MessageTypeExtra::KEXDH_GEX_REQUEST, |
1783 | NET_SSH2_MSG_KEXDH_GEX_REQUEST, |
1539 | $dh_group_sizes_packed |
1784 | $dh_group_sizes_packed |
1540 | ); |
1785 | ); |
1541 | $this->send_binary_packet($packet); |
1786 | $this->send_binary_packet($packet); |
1542 | $this->updateLogHistory('UNKNOWN (34)', 'SSH_MSG_KEXDH_GEX_REQUEST'); |
1787 | $this->updateLogHistory('UNKNOWN (34)', 'NET_SSH2_MSG_KEXDH_GEX_REQUEST'); |
1543 | 1788 | ||
1544 | $response = $this->get_binary_packet(); |
1789 | $response = $this->get_binary_packet(); |
1545 | 1790 | ||
1546 | list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response); |
1791 | list($type, $primeBytes, $gBytes) = Strings::unpackSSH2('Css', $response); |
1547 | if ($type != MessageTypeExtra::KEXDH_GEX_GROUP) { |
1792 | if ($type != NET_SSH2_MSG_KEXDH_GEX_GROUP) { |
1548 | $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR); |
1793 | $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); |
1549 | throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP'); |
1794 | throw new \UnexpectedValueException('Expected SSH_MSG_KEX_DH_GEX_GROUP'); |
1550 | } |
1795 | } |
1551 | $this->updateLogHistory('UNKNOWN (31)', 'SSH_MSG_KEXDH_GEX_GROUP'); |
1796 | $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEXDH_GEX_GROUP'); |
1552 | $prime = new BigInteger($primeBytes, -256); |
1797 | $prime = new BigInteger($primeBytes, -256); |
1553 | $g = new BigInteger($gBytes, -256); |
1798 | $g = new BigInteger($gBytes, -256); |
1554 | 1799 | ||
1555 | $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2( |
1800 | $exchange_hash_rfc4419 = $dh_group_sizes_packed . Strings::packSSH2( |
1556 | 'ss', |
1801 | 'ss', |
1557 | $primeBytes, |
1802 | $primeBytes, |
1558 | $gBytes |
1803 | $gBytes |
1559 | ); |
1804 | ); |
1560 | 1805 | ||
1561 | $params = DH::createParameters($prime, $g); |
1806 | $params = DH::createParameters($prime, $g); |
1562 | $clientKexInitMessage = MessageTypeExtra::KEXDH_GEX_INIT; |
1807 | $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_GEX_INIT'; |
1563 | $serverKexReplyMessage = MessageTypeExtra::KEXDH_GEX_REPLY; |
1808 | $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_GEX_REPLY'; |
1564 | } else { |
1809 | } else { |
1565 | $params = DH::createParameters($this->kex_algorithm); |
1810 | $params = DH::createParameters($this->kex_algorithm); |
1566 | $clientKexInitMessage = MessageType::KEXDH_INIT; |
1811 | $clientKexInitMessage = 'NET_SSH2_MSG_KEXDH_INIT'; |
1567 | $serverKexReplyMessage = MessageType::KEXDH_REPLY; |
1812 | $serverKexReplyMessage = 'NET_SSH2_MSG_KEXDH_REPLY'; |
1568 | } |
1813 | } |
1569 | 1814 | ||
1570 | $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength)); |
1815 | $keyLength = min($kexHash->getLengthInBytes(), max($encryptKeyLength, $decryptKeyLength)); |
1571 | 1816 | ||
1572 | $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength |
1817 | $ourPrivate = DH::createKey($params, 16 * $keyLength); // 2 * 8 * $keyLength |
1573 | $ourPublic = $ourPrivate->getPublicKey()->toBigInteger(); |
1818 | $ourPublic = $ourPrivate->getPublicKey()->toBigInteger(); |
1574 | $ourPublicBytes = $ourPublic->toBytes(true); |
1819 | $ourPublicBytes = $ourPublic->toBytes(true); |
1575 | } |
1820 | } |
1576 | 1821 | ||
1577 | $data = pack('CNa*', $clientKexInitMessage, strlen($ourPublicBytes), $ourPublicBytes); |
1822 | $data = pack('CNa*', constant($clientKexInitMessage), strlen($ourPublicBytes), $ourPublicBytes); |
1578 | 1823 | ||
1579 | $this->send_binary_packet($data); |
1824 | $this->send_binary_packet($data); |
1580 | 1825 | ||
1581 | switch ($clientKexInitMessage) { |
1826 | switch ($clientKexInitMessage) { |
1582 | case MessageTypeExtra::KEX_ECDH_INIT: |
1827 | case 'NET_SSH2_MSG_KEX_ECDH_INIT': |
1583 | $this->updateLogHistory('SSH_MSG_KEXDH_INIT', 'SSH_MSG_KEX_ECDH_INIT'); |
1828 | $this->updateLogHistory('NET_SSH2_MSG_KEXDH_INIT', 'NET_SSH2_MSG_KEX_ECDH_INIT'); |
1584 | break; |
1829 | break; |
1585 | case MessageTypeExtra::KEXDH_GEX_INIT: |
1830 | case 'NET_SSH2_MSG_KEXDH_GEX_INIT': |
1586 | $this->updateLogHistory('UNKNOWN (32)', 'SSH_MSG_KEXDH_GEX_INIT'); |
1831 | $this->updateLogHistory('UNKNOWN (32)', 'NET_SSH2_MSG_KEXDH_GEX_INIT'); |
1587 | } |
1832 | } |
1588 | 1833 | ||
1589 | $response = $this->get_binary_packet(); |
1834 | $response = $this->get_binary_packet(); |
1590 | 1835 | ||
1591 | list( |
1836 | list( |
Line 1593... | Line 1838... | ||
1593 | $server_public_host_key, |
1838 | $server_public_host_key, |
1594 | $theirPublicBytes, |
1839 | $theirPublicBytes, |
1595 | $this->signature |
1840 | $this->signature |
1596 | ) = Strings::unpackSSH2('Csss', $response); |
1841 | ) = Strings::unpackSSH2('Csss', $response); |
1597 | 1842 | ||
1598 | if ($type != $serverKexReplyMessage) { |
1843 | if ($type != constant($serverKexReplyMessage)) { |
1599 | $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR); |
1844 | $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); |
1600 | throw new \UnexpectedValueException("Expected $serverKexReplyMessage"); |
1845 | throw new \UnexpectedValueException("Expected $serverKexReplyMessage"); |
1601 | } |
1846 | } |
1602 | switch ($serverKexReplyMessage) { |
1847 | switch ($serverKexReplyMessage) { |
1603 | case MessageTypeExtra::KEX_ECDH_REPLY: |
1848 | case 'NET_SSH2_MSG_KEX_ECDH_REPLY': |
1604 | $this->updateLogHistory('SSH_MSG_KEXDH_REPLY', 'SSH_MSG_KEX_ECDH_REPLY'); |
1849 | $this->updateLogHistory('NET_SSH2_MSG_KEXDH_REPLY', 'NET_SSH2_MSG_KEX_ECDH_REPLY'); |
1605 | break; |
1850 | break; |
1606 | case MessageTypeExtra::KEXDH_GEX_REPLY: |
1851 | case 'NET_SSH2_MSG_KEXDH_GEX_REPLY': |
1607 | $this->updateLogHistory('UNKNOWN (33)', 'SSH_MSG_KEXDH_GEX_REPLY'); |
1852 | $this->updateLogHistory('UNKNOWN (33)', 'NET_SSH2_MSG_KEXDH_GEX_REPLY'); |
1608 | } |
1853 | } |
1609 | 1854 | ||
1610 | $this->server_public_host_key = $server_public_host_key; |
1855 | $this->server_public_host_key = $server_public_host_key; |
1611 | list($public_key_format) = Strings::unpackSSH2('s', $server_public_host_key); |
1856 | list($public_key_format) = Strings::unpackSSH2('s', $server_public_host_key); |
1612 | if (strlen($this->signature) < 4) { |
1857 | if (strlen($this->signature) < 4) { |
Line 1656... | Line 1901... | ||
1656 | if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { |
1901 | if ($public_key_format != $expected_key_format || $this->signature_format != $server_host_key_algorithm) { |
1657 | switch (true) { |
1902 | switch (true) { |
1658 | case $this->signature_format == $server_host_key_algorithm: |
1903 | case $this->signature_format == $server_host_key_algorithm: |
1659 | case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': |
1904 | case $server_host_key_algorithm != 'rsa-sha2-256' && $server_host_key_algorithm != 'rsa-sha2-512': |
1660 | case $this->signature_format != 'ssh-rsa': |
1905 | case $this->signature_format != 'ssh-rsa': |
1661 | $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE); |
1906 | $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); |
1662 | throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')'); |
1907 | throw new \RuntimeException('Server Host Key Algorithm Mismatch (' . $this->signature_format . ' vs ' . $server_host_key_algorithm . ')'); |
1663 | } |
1908 | } |
1664 | } |
1909 | } |
1665 | 1910 | ||
1666 | $packet = pack('C', MessageType::NEWKEYS); |
1911 | $packet = pack('C', NET_SSH2_MSG_NEWKEYS); |
1667 | $this->send_binary_packet($packet); |
1912 | $this->send_binary_packet($packet); |
1668 | 1913 | ||
1669 | $response = $this->get_binary_packet(); |
1914 | $response = $this->get_binary_packet(); |
1670 | 1915 | ||
1671 | if ($response === false) { |
1916 | if ($response === false) { |
1672 | $this->disconnect_helper(DisconnectReason::CONNECTION_LOST); |
1917 | $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); |
1673 | throw new ConnectionClosedException('Connection closed by server'); |
1918 | throw new ConnectionClosedException('Connection closed by server'); |
1674 | } |
1919 | } |
1675 | 1920 | ||
1676 | list($type) = Strings::unpackSSH2('C', $response); |
1921 | list($type) = Strings::unpackSSH2('C', $response); |
1677 | if ($type != MessageType::NEWKEYS) { |
1922 | if ($type != NET_SSH2_MSG_NEWKEYS) { |
1678 | $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR); |
1923 | $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); |
1679 | throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS'); |
1924 | throw new \UnexpectedValueException('Expected SSH_MSG_NEWKEYS'); |
1680 | } |
1925 | } |
1681 | 1926 | ||
1682 | $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); |
1927 | $keyBytes = pack('Na*', strlen($keyBytes), $keyBytes); |
1683 | 1928 | ||
Line 1834... | Line 2079... | ||
1834 | /** |
2079 | /** |
1835 | * Maps an encryption algorithm name to the number of key bytes. |
2080 | * Maps an encryption algorithm name to the number of key bytes. |
1836 | * |
2081 | * |
1837 | * @param string $algorithm Name of the encryption algorithm |
2082 | * @param string $algorithm Name of the encryption algorithm |
1838 | * @return int|null Number of bytes as an integer or null for unknown |
2083 | * @return int|null Number of bytes as an integer or null for unknown |
- | 2084 | * @access private |
|
1839 | */ |
2085 | */ |
1840 | private function encryption_algorithm_to_key_size($algorithm) |
2086 | private function encryption_algorithm_to_key_size($algorithm) |
1841 | { |
2087 | { |
1842 | if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) { |
2088 | if ($this->bad_key_size_fix && self::bad_algorithm_candidate($algorithm)) { |
1843 | return 16; |
2089 | return 16; |
Line 1881... | Line 2127... | ||
1881 | * Maps an encryption algorithm name to an instance of a subclass of |
2127 | * Maps an encryption algorithm name to an instance of a subclass of |
1882 | * \phpseclib3\Crypt\Common\SymmetricKey. |
2128 | * \phpseclib3\Crypt\Common\SymmetricKey. |
1883 | * |
2129 | * |
1884 | * @param string $algorithm Name of the encryption algorithm |
2130 | * @param string $algorithm Name of the encryption algorithm |
1885 | * @return SymmetricKey|null |
2131 | * @return SymmetricKey|null |
- | 2132 | * @access private |
|
1886 | */ |
2133 | */ |
1887 | private static function encryption_algorithm_to_crypt_instance($algorithm) |
2134 | private static function encryption_algorithm_to_crypt_instance($algorithm) |
1888 | { |
2135 | { |
1889 | switch ($algorithm) { |
2136 | switch ($algorithm) { |
1890 | case '3des-cbc': |
2137 | case '3des-cbc': |
Line 1929... | Line 2176... | ||
1929 | * Maps an encryption algorithm name to an instance of a subclass of |
2176 | * Maps an encryption algorithm name to an instance of a subclass of |
1930 | * \phpseclib3\Crypt\Hash. |
2177 | * \phpseclib3\Crypt\Hash. |
1931 | * |
2178 | * |
1932 | * @param string $algorithm Name of the encryption algorithm |
2179 | * @param string $algorithm Name of the encryption algorithm |
1933 | * @return array{Hash, int}|null |
2180 | * @return array{Hash, int}|null |
- | 2181 | * @access private |
|
1934 | */ |
2182 | */ |
1935 | private static function mac_algorithm_to_hash_instance($algorithm) |
2183 | private static function mac_algorithm_to_hash_instance($algorithm) |
1936 | { |
2184 | { |
1937 | switch ($algorithm) { |
2185 | switch ($algorithm) { |
1938 | case 'umac-64@openssh.com': |
2186 | case 'umac-64@openssh.com': |
Line 1964... | Line 2212... | ||
1964 | * |
2212 | * |
1965 | * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html |
2213 | * @link https://www.chiark.greenend.org.uk/~sgtatham/putty/wishlist/ssh2-aesctr-openssh.html |
1966 | * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291 |
2214 | * @link https://bugzilla.mindrot.org/show_bug.cgi?id=1291 |
1967 | * @param string $algorithm Name of the encryption algorithm |
2215 | * @param string $algorithm Name of the encryption algorithm |
1968 | * @return bool |
2216 | * @return bool |
- | 2217 | * @access private |
|
1969 | */ |
2218 | */ |
1970 | private static function bad_algorithm_candidate($algorithm) |
2219 | private static function bad_algorithm_candidate($algorithm) |
1971 | { |
2220 | { |
1972 | switch ($algorithm) { |
2221 | switch ($algorithm) { |
1973 | case 'arcfour256': |
2222 | case 'arcfour256': |
Line 1986... | Line 2235... | ||
1986 | * |
2235 | * |
1987 | * @param string $username |
2236 | * @param string $username |
1988 | * @param string|AsymmetricKey|array[]|Agent|null ...$args |
2237 | * @param string|AsymmetricKey|array[]|Agent|null ...$args |
1989 | * @return bool |
2238 | * @return bool |
1990 | * @see self::_login() |
2239 | * @see self::_login() |
- | 2240 | * @access public |
|
1991 | */ |
2241 | */ |
1992 | public function login($username, ...$args) |
2242 | public function login($username, ...$args) |
1993 | { |
2243 | { |
1994 | $this->auth[] = func_get_args(); |
2244 | $this->auth[] = func_get_args(); |
1995 | 2245 | ||
Line 2011... | Line 2261... | ||
2011 | * |
2261 | * |
2012 | * @param string $username |
2262 | * @param string $username |
2013 | * @param string ...$args |
2263 | * @param string ...$args |
2014 | * @return bool |
2264 | * @return bool |
2015 | * @see self::_login_helper() |
2265 | * @see self::_login_helper() |
- | 2266 | * @access private |
|
2016 | */ |
2267 | */ |
2017 | protected function sublogin($username, ...$args) |
2268 | protected function sublogin($username, ...$args) |
2018 | { |
2269 | { |
2019 | if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { |
2270 | if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { |
2020 | $this->connect(); |
2271 | $this->connect(); |
Line 2108... | Line 2359... | ||
2108 | * @param string $username |
2359 | * @param string $username |
2109 | * @param string|AsymmetricKey|array[]|Agent|null ...$args |
2360 | * @param string|AsymmetricKey|array[]|Agent|null ...$args |
2110 | * @return bool |
2361 | * @return bool |
2111 | * @throws \UnexpectedValueException on receipt of unexpected packets |
2362 | * @throws \UnexpectedValueException on receipt of unexpected packets |
2112 | * @throws \RuntimeException on other errors |
2363 | * @throws \RuntimeException on other errors |
- | 2364 | * @access private |
|
2113 | */ |
2365 | */ |
2114 | private function login_helper($username, $password = null) |
2366 | private function login_helper($username, $password = null) |
2115 | { |
2367 | { |
2116 | if (!($this->bitmap & self::MASK_CONNECTED)) { |
2368 | if (!($this->bitmap & self::MASK_CONNECTED)) { |
2117 | return false; |
2369 | return false; |
2118 | } |
2370 | } |
2119 | 2371 | ||
2120 | if (!($this->bitmap & self::MASK_LOGIN_REQ)) { |
2372 | if (!($this->bitmap & self::MASK_LOGIN_REQ)) { |
2121 | $packet = Strings::packSSH2('Cs', MessageType::SERVICE_REQUEST, 'ssh-userauth'); |
2373 | $packet = Strings::packSSH2('Cs', NET_SSH2_MSG_SERVICE_REQUEST, 'ssh-userauth'); |
2122 | $this->send_binary_packet($packet); |
2374 | $this->send_binary_packet($packet); |
2123 | 2375 | ||
2124 | try { |
2376 | try { |
2125 | $response = $this->get_binary_packet(); |
2377 | $response = $this->get_binary_packet(); |
2126 | } catch (\Exception $e) { |
2378 | } catch (\Exception $e) { |
2127 | if ($this->retry_connect) { |
2379 | if ($this->retry_connect) { |
2128 | $this->retry_connect = false; |
2380 | $this->retry_connect = false; |
2129 | $this->connect(); |
2381 | $this->connect(); |
2130 | return $this->login_helper($username, $password); |
2382 | return $this->login_helper($username, $password); |
2131 | } |
2383 | } |
2132 | $this->disconnect_helper(DisconnectReason::CONNECTION_LOST); |
2384 | $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); |
2133 | throw new ConnectionClosedException('Connection closed by server'); |
2385 | throw new ConnectionClosedException('Connection closed by server'); |
2134 | } |
2386 | } |
2135 | 2387 | ||
2136 | list($type, $service) = Strings::unpackSSH2('Cs', $response); |
2388 | list($type, $service) = Strings::unpackSSH2('Cs', $response); |
2137 | if ($type != MessageType::SERVICE_ACCEPT || $service != 'ssh-userauth') { |
2389 | if ($type != NET_SSH2_MSG_SERVICE_ACCEPT || $service != 'ssh-userauth') { |
2138 | $this->disconnect_helper(DisconnectReason::PROTOCOL_ERROR); |
2390 | $this->disconnect_helper(NET_SSH2_DISCONNECT_PROTOCOL_ERROR); |
2139 | throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); |
2391 | throw new \UnexpectedValueException('Expected SSH_MSG_SERVICE_ACCEPT'); |
2140 | } |
2392 | } |
2141 | $this->bitmap |= self::MASK_LOGIN_REQ; |
2393 | $this->bitmap |= self::MASK_LOGIN_REQ; |
2142 | } |
2394 | } |
2143 | 2395 | ||
Line 2162... | Line 2414... | ||
2162 | } |
2414 | } |
2163 | 2415 | ||
2164 | if (!isset($password)) { |
2416 | if (!isset($password)) { |
2165 | $packet = Strings::packSSH2( |
2417 | $packet = Strings::packSSH2( |
2166 | 'Cs3', |
2418 | 'Cs3', |
2167 | MessageType::USERAUTH_REQUEST, |
2419 | NET_SSH2_MSG_USERAUTH_REQUEST, |
2168 | $username, |
2420 | $username, |
2169 | 'ssh-connection', |
2421 | 'ssh-connection', |
2170 | 'none' |
2422 | 'none' |
2171 | ); |
2423 | ); |
2172 | 2424 | ||
Line 2174... | Line 2426... | ||
2174 | 2426 | ||
2175 | $response = $this->get_binary_packet(); |
2427 | $response = $this->get_binary_packet(); |
2176 | 2428 | ||
2177 | list($type) = Strings::unpackSSH2('C', $response); |
2429 | list($type) = Strings::unpackSSH2('C', $response); |
2178 | switch ($type) { |
2430 | switch ($type) { |
2179 | case MessageType::USERAUTH_SUCCESS: |
2431 | case NET_SSH2_MSG_USERAUTH_SUCCESS: |
2180 | $this->bitmap |= self::MASK_LOGIN; |
2432 | $this->bitmap |= self::MASK_LOGIN; |
2181 | return true; |
2433 | return true; |
2182 | case MessageType::USERAUTH_FAILURE: |
2434 | case NET_SSH2_MSG_USERAUTH_FAILURE: |
2183 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2435 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2184 | $this->auth_methods_to_continue = $auth_methods; |
2436 | $this->auth_methods_to_continue = $auth_methods; |
2185 | // fall-through |
2437 | // fall-through |
2186 | default: |
2438 | default: |
2187 | return false; |
2439 | return false; |
2188 | } |
2440 | } |
2189 | } |
2441 | } |
2190 | 2442 | ||
2191 | $packet = Strings::packSSH2( |
2443 | $packet = Strings::packSSH2( |
2192 | 'Cs3bs', |
2444 | 'Cs3bs', |
2193 | MessageType::USERAUTH_REQUEST, |
2445 | NET_SSH2_MSG_USERAUTH_REQUEST, |
2194 | $username, |
2446 | $username, |
2195 | 'ssh-connection', |
2447 | 'ssh-connection', |
2196 | 'password', |
2448 | 'password', |
2197 | false, |
2449 | false, |
2198 | $password |
2450 | $password |
Line 2202... | Line 2454... | ||
2202 | if (!defined('NET_SSH2_LOGGING')) { |
2454 | if (!defined('NET_SSH2_LOGGING')) { |
2203 | $logged = null; |
2455 | $logged = null; |
2204 | } else { |
2456 | } else { |
2205 | $logged = Strings::packSSH2( |
2457 | $logged = Strings::packSSH2( |
2206 | 'Cs3bs', |
2458 | 'Cs3bs', |
2207 | MessageType::USERAUTH_REQUEST, |
2459 | NET_SSH2_MSG_USERAUTH_REQUEST, |
2208 | $username, |
2460 | $username, |
2209 | 'ssh-connection', |
2461 | 'ssh-connection', |
2210 | 'password', |
2462 | 'password', |
2211 | false, |
2463 | false, |
2212 | 'password' |
2464 | 'password' |
Line 2214... | Line 2466... | ||
2214 | } |
2466 | } |
2215 | 2467 | ||
2216 | $this->send_binary_packet($packet, $logged); |
2468 | $this->send_binary_packet($packet, $logged); |
2217 | 2469 | ||
2218 | $response = $this->get_binary_packet(); |
2470 | $response = $this->get_binary_packet(); |
2219 | if ($response === false) { |
- | |
2220 | return false; |
- | |
2221 | } |
2471 | |
2222 | list($type) = Strings::unpackSSH2('C', $response); |
2472 | list($type) = Strings::unpackSSH2('C', $response); |
2223 | switch ($type) { |
2473 | switch ($type) { |
2224 | case MessageTypeExtra::USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed |
2474 | case NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ: // in theory, the password can be changed |
2225 | $this->updateLogHistory('SSH_MSG_USERAUTH_INFO_REQUEST', 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ'); |
2475 | $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PASSWD_CHANGEREQ'); |
2226 | 2476 | ||
2227 | list($message) = Strings::unpackSSH2('s', $response); |
2477 | list($message) = Strings::unpackSSH2('s', $response); |
2228 | $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message; |
2478 | $this->errors[] = 'SSH_MSG_USERAUTH_PASSWD_CHANGEREQ: ' . $message; |
2229 | 2479 | ||
2230 | return $this->disconnect_helper(DisconnectReason::AUTH_CANCELLED_BY_USER); |
2480 | return $this->disconnect_helper(NET_SSH2_DISCONNECT_AUTH_CANCELLED_BY_USER); |
2231 | case MessageType::USERAUTH_FAILURE: |
2481 | case NET_SSH2_MSG_USERAUTH_FAILURE: |
2232 | // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees |
2482 | // can we use keyboard-interactive authentication? if not then either the login is bad or the server employees |
2233 | // multi-factor authentication |
2483 | // multi-factor authentication |
2234 | list($auth_methods, $partial_success) = Strings::unpackSSH2('Lb', $response); |
2484 | list($auth_methods, $partial_success) = Strings::unpackSSH2('Lb', $response); |
2235 | $this->auth_methods_to_continue = $auth_methods; |
2485 | $this->auth_methods_to_continue = $auth_methods; |
2236 | if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { |
2486 | if (!$partial_success && in_array('keyboard-interactive', $auth_methods)) { |
Line 2239... | Line 2489... | ||
2239 | return true; |
2489 | return true; |
2240 | } |
2490 | } |
2241 | return false; |
2491 | return false; |
2242 | } |
2492 | } |
2243 | return false; |
2493 | return false; |
2244 | case MessageType::USERAUTH_SUCCESS: |
2494 | case NET_SSH2_MSG_USERAUTH_SUCCESS: |
2245 | $this->bitmap |= self::MASK_LOGIN; |
2495 | $this->bitmap |= self::MASK_LOGIN; |
2246 | return true; |
2496 | return true; |
2247 | } |
2497 | } |
2248 | 2498 | ||
2249 | return false; |
2499 | return false; |
Line 2255... | Line 2505... | ||
2255 | * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. |
2505 | * See {@link http://tools.ietf.org/html/rfc4256 RFC4256} for details. This is not a full-featured keyboard-interactive authenticator. |
2256 | * |
2506 | * |
2257 | * @param string $username |
2507 | * @param string $username |
2258 | * @param string|array $password |
2508 | * @param string|array $password |
2259 | * @return bool |
2509 | * @return bool |
- | 2510 | * @access private |
|
2260 | */ |
2511 | */ |
2261 | private function keyboard_interactive_login($username, $password) |
2512 | private function keyboard_interactive_login($username, $password) |
2262 | { |
2513 | { |
2263 | $packet = Strings::packSSH2( |
2514 | $packet = Strings::packSSH2( |
2264 | 'Cs5', |
2515 | 'Cs5', |
2265 | MessageType::USERAUTH_REQUEST, |
2516 | NET_SSH2_MSG_USERAUTH_REQUEST, |
2266 | $username, |
2517 | $username, |
2267 | 'ssh-connection', |
2518 | 'ssh-connection', |
2268 | 'keyboard-interactive', |
2519 | 'keyboard-interactive', |
2269 | '', // language tag |
2520 | '', // language tag |
2270 | '' // submethods |
2521 | '' // submethods |
Line 2278... | Line 2529... | ||
2278 | * Handle the keyboard-interactive requests / responses. |
2529 | * Handle the keyboard-interactive requests / responses. |
2279 | * |
2530 | * |
2280 | * @param string|array ...$responses |
2531 | * @param string|array ...$responses |
2281 | * @return bool |
2532 | * @return bool |
2282 | * @throws \RuntimeException on connection error |
2533 | * @throws \RuntimeException on connection error |
- | 2534 | * @access private |
|
2283 | */ |
2535 | */ |
2284 | private function keyboard_interactive_process(...$responses) |
2536 | private function keyboard_interactive_process(...$responses) |
2285 | { |
2537 | { |
2286 | if (strlen($this->last_interactive_response)) { |
2538 | if (strlen($this->last_interactive_response)) { |
2287 | $response = $this->last_interactive_response; |
2539 | $response = $this->last_interactive_response; |
Line 2289... | Line 2541... | ||
2289 | $orig = $response = $this->get_binary_packet(); |
2541 | $orig = $response = $this->get_binary_packet(); |
2290 | } |
2542 | } |
2291 | 2543 | ||
2292 | list($type) = Strings::unpackSSH2('C', $response); |
2544 | list($type) = Strings::unpackSSH2('C', $response); |
2293 | switch ($type) { |
2545 | switch ($type) { |
2294 | case MessageType::USERAUTH_INFO_REQUEST: |
2546 | case NET_SSH2_MSG_USERAUTH_INFO_REQUEST: |
2295 | list( |
2547 | list( |
2296 | , // name; may be empty |
2548 | , // name; may be empty |
2297 | , // instruction; may be empty |
2549 | , // instruction; may be empty |
2298 | , // language tag; may be empty |
2550 | , // language tag; may be empty |
2299 | $num_prompts |
2551 | $num_prompts |
Line 2326... | Line 2578... | ||
2326 | 2578 | ||
2327 | // see http://tools.ietf.org/html/rfc4256#section-3.2 |
2579 | // see http://tools.ietf.org/html/rfc4256#section-3.2 |
2328 | if (strlen($this->last_interactive_response)) { |
2580 | if (strlen($this->last_interactive_response)) { |
2329 | $this->last_interactive_response = ''; |
2581 | $this->last_interactive_response = ''; |
2330 | } else { |
2582 | } else { |
2331 | $this->updateLogHistory('UNKNOWN (60)', 'SSH_MSG_USERAUTH_INFO_REQUEST'); |
2583 | $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_INFO_REQUEST'); |
2332 | } |
2584 | } |
2333 | 2585 | ||
2334 | if (!count($responses) && $num_prompts) { |
2586 | if (!count($responses) && $num_prompts) { |
2335 | $this->last_interactive_response = $orig; |
2587 | $this->last_interactive_response = $orig; |
2336 | return false; |
2588 | return false; |
Line 2339... | Line 2591... | ||
2339 | /* |
2591 | /* |
2340 | After obtaining the requested information from the user, the client |
2592 | After obtaining the requested information from the user, the client |
2341 | MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. |
2593 | MUST respond with an SSH_MSG_USERAUTH_INFO_RESPONSE message. |
2342 | */ |
2594 | */ |
2343 | // see http://tools.ietf.org/html/rfc4256#section-3.4 |
2595 | // see http://tools.ietf.org/html/rfc4256#section-3.4 |
2344 | $packet = $logged = pack('CN', MessageType::USERAUTH_INFO_RESPONSE, count($responses)); |
2596 | $packet = $logged = pack('CN', NET_SSH2_MSG_USERAUTH_INFO_RESPONSE, count($responses)); |
2345 | for ($i = 0; $i < count($responses); $i++) { |
2597 | for ($i = 0; $i < count($responses); $i++) { |
2346 | $packet .= Strings::packSSH2('s', $responses[$i]); |
2598 | $packet .= Strings::packSSH2('s', $responses[$i]); |
2347 | $logged .= Strings::packSSH2('s', 'dummy-answer'); |
2599 | $logged .= Strings::packSSH2('s', 'dummy-answer'); |
2348 | } |
2600 | } |
2349 | 2601 | ||
2350 | $this->send_binary_packet($packet, $logged); |
2602 | $this->send_binary_packet($packet, $logged); |
2351 | 2603 | ||
2352 | $this->updateLogHistory('UNKNOWN (61)', 'SSH_MSG_USERAUTH_INFO_RESPONSE'); |
2604 | $this->updateLogHistory('UNKNOWN (61)', 'NET_SSH2_MSG_USERAUTH_INFO_RESPONSE'); |
2353 | 2605 | ||
2354 | /* |
2606 | /* |
2355 | After receiving the response, the server MUST send either an |
2607 | After receiving the response, the server MUST send either an |
2356 | SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another |
2608 | SSH_MSG_USERAUTH_SUCCESS, SSH_MSG_USERAUTH_FAILURE, or another |
2357 | SSH_MSG_USERAUTH_INFO_REQUEST message. |
2609 | SSH_MSG_USERAUTH_INFO_REQUEST message. |
2358 | */ |
2610 | */ |
2359 | // maybe phpseclib should force close the connection after x request / responses? unless something like that is done |
2611 | // maybe phpseclib should force close the connection after x request / responses? unless something like that is done |
2360 | // there could be an infinite loop of request / responses. |
2612 | // there could be an infinite loop of request / responses. |
2361 | return $this->keyboard_interactive_process(); |
2613 | return $this->keyboard_interactive_process(); |
2362 | case MessageType::USERAUTH_SUCCESS: |
2614 | case NET_SSH2_MSG_USERAUTH_SUCCESS: |
2363 | return true; |
2615 | return true; |
2364 | case MessageType::USERAUTH_FAILURE: |
2616 | case NET_SSH2_MSG_USERAUTH_FAILURE: |
2365 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2617 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2366 | $this->auth_methods_to_continue = $auth_methods; |
2618 | $this->auth_methods_to_continue = $auth_methods; |
2367 | return false; |
2619 | return false; |
2368 | } |
2620 | } |
2369 | 2621 | ||
Line 2374... | Line 2626... | ||
2374 | * Login with an ssh-agent provided key |
2626 | * Login with an ssh-agent provided key |
2375 | * |
2627 | * |
2376 | * @param string $username |
2628 | * @param string $username |
2377 | * @param \phpseclib3\System\SSH\Agent $agent |
2629 | * @param \phpseclib3\System\SSH\Agent $agent |
2378 | * @return bool |
2630 | * @return bool |
- | 2631 | * @access private |
|
2379 | */ |
2632 | */ |
2380 | private function ssh_agent_login($username, Agent $agent) |
2633 | private function ssh_agent_login($username, Agent $agent) |
2381 | { |
2634 | { |
2382 | $this->agent = $agent; |
2635 | $this->agent = $agent; |
2383 | $keys = $agent->requestIdentities(); |
2636 | $keys = $agent->requestIdentities(); |
Line 2398... | Line 2651... | ||
2398 | * |
2651 | * |
2399 | * @param string $username |
2652 | * @param string $username |
2400 | * @param \phpseclib3\Crypt\Common\PrivateKey $privatekey |
2653 | * @param \phpseclib3\Crypt\Common\PrivateKey $privatekey |
2401 | * @return bool |
2654 | * @return bool |
2402 | * @throws \RuntimeException on connection error |
2655 | * @throws \RuntimeException on connection error |
- | 2656 | * @access private |
|
2403 | */ |
2657 | */ |
2404 | private function privatekey_login($username, PrivateKey $privatekey) |
2658 | private function privatekey_login($username, PrivateKey $privatekey) |
2405 | { |
2659 | { |
2406 | $publickey = $privatekey->getPublicKey(); |
2660 | $publickey = $privatekey->getPublicKey(); |
2407 | 2661 | ||
Line 2462... | Line 2716... | ||
2462 | 2716 | ||
2463 | $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]); |
2717 | $publickeyStr = $publickey->toString('OpenSSH', ['binary' => true]); |
2464 | 2718 | ||
2465 | $part1 = Strings::packSSH2( |
2719 | $part1 = Strings::packSSH2( |
2466 | 'Csss', |
2720 | 'Csss', |
2467 | MessageType::USERAUTH_REQUEST, |
2721 | NET_SSH2_MSG_USERAUTH_REQUEST, |
2468 | $username, |
2722 | $username, |
2469 | 'ssh-connection', |
2723 | 'ssh-connection', |
2470 | 'publickey' |
2724 | 'publickey' |
2471 | ); |
2725 | ); |
2472 | $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr); |
2726 | $part2 = Strings::packSSH2('ss', $signatureType, $publickeyStr); |
Line 2476... | Line 2730... | ||
2476 | 2730 | ||
2477 | $response = $this->get_binary_packet(); |
2731 | $response = $this->get_binary_packet(); |
2478 | 2732 | ||
2479 | list($type) = Strings::unpackSSH2('C', $response); |
2733 | list($type) = Strings::unpackSSH2('C', $response); |
2480 | switch ($type) { |
2734 | switch ($type) { |
2481 | case MessageType::USERAUTH_FAILURE: |
2735 | case NET_SSH2_MSG_USERAUTH_FAILURE: |
2482 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2736 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2483 | $this->auth_methods_to_continue = $auth_methods; |
2737 | $this->auth_methods_to_continue = $auth_methods; |
2484 | $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; |
2738 | $this->errors[] = 'SSH_MSG_USERAUTH_FAILURE'; |
2485 | return false; |
2739 | return false; |
2486 | case MessageTypeExtra::USERAUTH_PK_OK: |
2740 | case NET_SSH2_MSG_USERAUTH_PK_OK: |
2487 | // we'll just take it on faith that the public key blob and the public key algorithm name are as |
2741 | // we'll just take it on faith that the public key blob and the public key algorithm name are as |
2488 | // they should be |
2742 | // they should be |
2489 | $this->updateLogHistory('SSH_MSG_USERAUTH_INFO_REQUEST', 'SSH_MSG_USERAUTH_PK_OK'); |
2743 | $this->updateLogHistory('UNKNOWN (60)', 'NET_SSH2_MSG_USERAUTH_PK_OK'); |
2490 | break; |
2744 | break; |
2491 | case MessageType::USERAUTH_SUCCESS: |
2745 | case NET_SSH2_MSG_USERAUTH_SUCCESS: |
2492 | $this->bitmap |= self::MASK_LOGIN; |
2746 | $this->bitmap |= self::MASK_LOGIN; |
2493 | return true; |
2747 | return true; |
2494 | default: |
2748 | default: |
2495 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
2749 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
2496 | throw new ConnectionClosedException('Unexpected response to publickey authentication pt 1'); |
2750 | throw new ConnectionClosedException('Unexpected response to publickey authentication pt 1'); |
2497 | } |
2751 | } |
2498 | 2752 | ||
2499 | $packet = $part1 . chr(1) . $part2; |
2753 | $packet = $part1 . chr(1) . $part2; |
2500 | $privatekey = $privatekey->withHash($hash); |
2754 | $privatekey = $privatekey->withHash($hash); |
Line 2508... | Line 2762... | ||
2508 | 2762 | ||
2509 | $response = $this->get_binary_packet(); |
2763 | $response = $this->get_binary_packet(); |
2510 | 2764 | ||
2511 | list($type) = Strings::unpackSSH2('C', $response); |
2765 | list($type) = Strings::unpackSSH2('C', $response); |
2512 | switch ($type) { |
2766 | switch ($type) { |
2513 | case MessageType::USERAUTH_FAILURE: |
2767 | case NET_SSH2_MSG_USERAUTH_FAILURE: |
2514 | // either the login is bad or the server employs multi-factor authentication |
2768 | // either the login is bad or the server employs multi-factor authentication |
2515 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2769 | list($auth_methods) = Strings::unpackSSH2('L', $response); |
2516 | $this->auth_methods_to_continue = $auth_methods; |
2770 | $this->auth_methods_to_continue = $auth_methods; |
2517 | return false; |
2771 | return false; |
2518 | case MessageType::USERAUTH_SUCCESS: |
2772 | case NET_SSH2_MSG_USERAUTH_SUCCESS: |
2519 | $this->bitmap |= self::MASK_LOGIN; |
2773 | $this->bitmap |= self::MASK_LOGIN; |
2520 | return true; |
2774 | return true; |
2521 | } |
2775 | } |
2522 | 2776 | ||
2523 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
2777 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
2524 | throw new ConnectionClosedException('Unexpected response to publickey authentication pt 2'); |
2778 | throw new ConnectionClosedException('Unexpected response to publickey authentication pt 2'); |
2525 | } |
2779 | } |
2526 | 2780 | ||
2527 | /** |
2781 | /** |
2528 | * Set Timeout |
2782 | * Set Timeout |
2529 | * |
2783 | * |
2530 | * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. |
2784 | * $ssh->exec('ping 127.0.0.1'); on a Linux host will never return and will run indefinitely. setTimeout() makes it so it'll timeout. |
2531 | * Setting $timeout to false or 0 will mean there is no timeout. |
2785 | * Setting $timeout to false or 0 will mean there is no timeout. |
2532 | * |
2786 | * |
2533 | * @param mixed $timeout |
2787 | * @param mixed $timeout |
- | 2788 | * @access public |
|
2534 | */ |
2789 | */ |
2535 | public function setTimeout($timeout) |
2790 | public function setTimeout($timeout) |
2536 | { |
2791 | { |
2537 | $this->timeout = $this->curTimeout = $timeout; |
2792 | $this->timeout = $this->curTimeout = $timeout; |
2538 | } |
2793 | } |
Line 2541... | Line 2796... | ||
2541 | * Set Keep Alive |
2796 | * Set Keep Alive |
2542 | * |
2797 | * |
2543 | * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. |
2798 | * Sends an SSH2_MSG_IGNORE message every x seconds, if x is a positive non-zero number. |
2544 | * |
2799 | * |
2545 | * @param int $interval |
2800 | * @param int $interval |
- | 2801 | * @access public |
|
2546 | */ |
2802 | */ |
2547 | public function setKeepAlive($interval) |
2803 | public function setKeepAlive($interval) |
2548 | { |
2804 | { |
2549 | $this->keepAlive = $interval; |
2805 | $this->keepAlive = $interval; |
2550 | } |
2806 | } |
2551 | 2807 | ||
2552 | /** |
2808 | /** |
2553 | * Get the output from stdError |
2809 | * Get the output from stdError |
2554 | * |
2810 | * |
- | 2811 | * @access public |
|
2555 | */ |
2812 | */ |
2556 | public function getStdError() |
2813 | public function getStdError() |
2557 | { |
2814 | { |
2558 | return $this->stdErrorLog; |
2815 | return $this->stdErrorLog; |
2559 | } |
2816 | } |
Line 2566... | Line 2823... | ||
2566 | * |
2823 | * |
2567 | * @param string $command |
2824 | * @param string $command |
2568 | * @return string|bool |
2825 | * @return string|bool |
2569 | * @psalm-return ($callback is callable ? bool : string|bool) |
2826 | * @psalm-return ($callback is callable ? bool : string|bool) |
2570 | * @throws \RuntimeException on connection error |
2827 | * @throws \RuntimeException on connection error |
- | 2828 | * @access public |
|
2571 | */ |
2829 | */ |
2572 | public function exec($command, callable $callback = null) |
2830 | public function exec($command, callable $callback = null) |
2573 | { |
2831 | { |
2574 | $this->curTimeout = $this->timeout; |
2832 | $this->curTimeout = $this->timeout; |
2575 | $this->is_timeout = false; |
2833 | $this->is_timeout = false; |
Line 2592... | Line 2850... | ||
2592 | // uses 0x4000, that's what will be used here, as well. |
2850 | // uses 0x4000, that's what will be used here, as well. |
2593 | $packet_size = 0x4000; |
2851 | $packet_size = 0x4000; |
2594 | 2852 | ||
2595 | $packet = Strings::packSSH2( |
2853 | $packet = Strings::packSSH2( |
2596 | 'CsN3', |
2854 | 'CsN3', |
2597 | MessageType::CHANNEL_OPEN, |
2855 | NET_SSH2_MSG_CHANNEL_OPEN, |
2598 | 'session', |
2856 | 'session', |
2599 | self::CHANNEL_EXEC, |
2857 | self::CHANNEL_EXEC, |
2600 | $this->window_size_server_to_client[self::CHANNEL_EXEC], |
2858 | $this->window_size_server_to_client[self::CHANNEL_EXEC], |
2601 | $packet_size |
2859 | $packet_size |
2602 | ); |
2860 | ); |
2603 | $this->send_binary_packet($packet); |
2861 | $this->send_binary_packet($packet); |
2604 | 2862 | ||
2605 | $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_OPEN; |
2863 | $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_OPEN; |
2606 | 2864 | ||
2607 | $this->get_channel_packet(self::CHANNEL_EXEC); |
2865 | $this->get_channel_packet(self::CHANNEL_EXEC); |
2608 | 2866 | ||
2609 | if ($this->request_pty === true) { |
2867 | if ($this->request_pty === true) { |
2610 | $terminal_modes = pack('C', TerminalMode::TTY_OP_END); |
2868 | $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); |
2611 | $packet = Strings::packSSH2( |
2869 | $packet = Strings::packSSH2( |
2612 | 'CNsCsN4s', |
2870 | 'CNsCsN4s', |
2613 | MessageType::CHANNEL_REQUEST, |
2871 | NET_SSH2_MSG_CHANNEL_REQUEST, |
2614 | $this->server_channels[self::CHANNEL_EXEC], |
2872 | $this->server_channels[self::CHANNEL_EXEC], |
2615 | 'pty-req', |
2873 | 'pty-req', |
2616 | 1, |
2874 | 1, |
2617 | $this->term, |
2875 | $this->term, |
2618 | $this->windowColumns, |
2876 | $this->windowColumns, |
Line 2622... | Line 2880... | ||
2622 | $terminal_modes |
2880 | $terminal_modes |
2623 | ); |
2881 | ); |
2624 | 2882 | ||
2625 | $this->send_binary_packet($packet); |
2883 | $this->send_binary_packet($packet); |
2626 | 2884 | ||
2627 | $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_REQUEST; |
2885 | $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; |
2628 | if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { |
2886 | if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { |
2629 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
2887 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
2630 | throw new \RuntimeException('Unable to request pseudo-terminal'); |
2888 | throw new \RuntimeException('Unable to request pseudo-terminal'); |
2631 | } |
2889 | } |
2632 | 2890 | ||
2633 | $this->in_request_pty_exec = true; |
2891 | $this->in_request_pty_exec = true; |
2634 | } |
2892 | } |
Line 2642... | Line 2900... | ||
2642 | // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by |
2900 | // although, in theory, the size of SSH_MSG_CHANNEL_REQUEST could exceed the maximum packet size established by |
2643 | // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the |
2901 | // SSH_MSG_CHANNEL_OPEN_CONFIRMATION, RFC4254#section-5.1 states that the "maximum packet size" refers to the |
2644 | // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. |
2902 | // "maximum size of an individual data packet". ie. SSH_MSG_CHANNEL_DATA. RFC4254#section-5.2 corroborates. |
2645 | $packet = Strings::packSSH2( |
2903 | $packet = Strings::packSSH2( |
2646 | 'CNsCs', |
2904 | 'CNsCs', |
2647 | MessageType::CHANNEL_REQUEST, |
2905 | NET_SSH2_MSG_CHANNEL_REQUEST, |
2648 | $this->server_channels[self::CHANNEL_EXEC], |
2906 | $this->server_channels[self::CHANNEL_EXEC], |
2649 | 'exec', |
2907 | 'exec', |
2650 | 1, |
2908 | 1, |
2651 | $command |
2909 | $command |
2652 | ); |
2910 | ); |
2653 | $this->send_binary_packet($packet); |
2911 | $this->send_binary_packet($packet); |
2654 | 2912 | ||
2655 | $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_REQUEST; |
2913 | $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_REQUEST; |
2656 | 2914 | ||
2657 | if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { |
2915 | if (!$this->get_channel_packet(self::CHANNEL_EXEC)) { |
2658 | return false; |
2916 | return false; |
2659 | } |
2917 | } |
2660 | 2918 | ||
2661 | $this->channel_status[self::CHANNEL_EXEC] = MessageType::CHANNEL_DATA; |
2919 | $this->channel_status[self::CHANNEL_EXEC] = NET_SSH2_MSG_CHANNEL_DATA; |
2662 | 2920 | ||
2663 | if ($callback === false || $this->in_request_pty_exec) { |
2921 | if ($callback === false || $this->in_request_pty_exec) { |
2664 | return true; |
2922 | return true; |
2665 | } |
2923 | } |
2666 | 2924 | ||
Line 2691... | Line 2949... | ||
2691 | * @see self::read() |
2949 | * @see self::read() |
2692 | * @see self::write() |
2950 | * @see self::write() |
2693 | * @return bool |
2951 | * @return bool |
2694 | * @throws \UnexpectedValueException on receipt of unexpected packets |
2952 | * @throws \UnexpectedValueException on receipt of unexpected packets |
2695 | * @throws \RuntimeException on other errors |
2953 | * @throws \RuntimeException on other errors |
- | 2954 | * @access private |
|
2696 | */ |
2955 | */ |
2697 | private function initShell() |
2956 | private function initShell() |
2698 | { |
2957 | { |
2699 | if ($this->in_request_pty_exec === true) { |
2958 | if ($this->in_request_pty_exec === true) { |
2700 | return true; |
2959 | return true; |
Line 2703... | Line 2962... | ||
2703 | $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; |
2962 | $this->window_size_server_to_client[self::CHANNEL_SHELL] = $this->window_size; |
2704 | $packet_size = 0x4000; |
2963 | $packet_size = 0x4000; |
2705 | 2964 | ||
2706 | $packet = Strings::packSSH2( |
2965 | $packet = Strings::packSSH2( |
2707 | 'CsN3', |
2966 | 'CsN3', |
2708 | MessageType::CHANNEL_OPEN, |
2967 | NET_SSH2_MSG_CHANNEL_OPEN, |
2709 | 'session', |
2968 | 'session', |
2710 | self::CHANNEL_SHELL, |
2969 | self::CHANNEL_SHELL, |
2711 | $this->window_size_server_to_client[self::CHANNEL_SHELL], |
2970 | $this->window_size_server_to_client[self::CHANNEL_SHELL], |
2712 | $packet_size |
2971 | $packet_size |
2713 | ); |
2972 | ); |
2714 | 2973 | ||
2715 | $this->send_binary_packet($packet); |
2974 | $this->send_binary_packet($packet); |
2716 | 2975 | ||
2717 | $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_OPEN; |
2976 | $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_OPEN; |
2718 | 2977 | ||
2719 | $this->get_channel_packet(self::CHANNEL_SHELL); |
2978 | $this->get_channel_packet(self::CHANNEL_SHELL); |
2720 | 2979 | ||
2721 | $terminal_modes = pack('C', TerminalMode::TTY_OP_END); |
2980 | $terminal_modes = pack('C', NET_SSH2_TTY_OP_END); |
2722 | $packet = Strings::packSSH2( |
2981 | $packet = Strings::packSSH2( |
2723 | 'CNsbsN4s', |
2982 | 'CNsbsN4s', |
2724 | MessageType::CHANNEL_REQUEST, |
2983 | NET_SSH2_MSG_CHANNEL_REQUEST, |
2725 | $this->server_channels[self::CHANNEL_SHELL], |
2984 | $this->server_channels[self::CHANNEL_SHELL], |
2726 | 'pty-req', |
2985 | 'pty-req', |
2727 | true, // want reply |
2986 | true, // want reply |
2728 | $this->term, |
2987 | $this->term, |
2729 | $this->windowColumns, |
2988 | $this->windowColumns, |
Line 2733... | Line 2992... | ||
2733 | $terminal_modes |
2992 | $terminal_modes |
2734 | ); |
2993 | ); |
2735 | 2994 | ||
2736 | $this->send_binary_packet($packet); |
2995 | $this->send_binary_packet($packet); |
2737 | 2996 | ||
2738 | $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_REQUEST; |
2997 | $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_REQUEST; |
2739 | 2998 | ||
2740 | if (!$this->get_channel_packet(self::CHANNEL_SHELL)) { |
2999 | if (!$this->get_channel_packet(self::CHANNEL_SHELL)) { |
2741 | throw new \RuntimeException('Unable to request pty'); |
3000 | throw new \RuntimeException('Unable to request pty'); |
2742 | } |
3001 | } |
2743 | 3002 | ||
2744 | $packet = Strings::packSSH2( |
3003 | $packet = Strings::packSSH2( |
2745 | 'CNsb', |
3004 | 'CNsb', |
2746 | MessageType::CHANNEL_REQUEST, |
3005 | NET_SSH2_MSG_CHANNEL_REQUEST, |
2747 | $this->server_channels[self::CHANNEL_SHELL], |
3006 | $this->server_channels[self::CHANNEL_SHELL], |
2748 | 'shell', |
3007 | 'shell', |
2749 | true // want reply |
3008 | true // want reply |
2750 | ); |
3009 | ); |
2751 | $this->send_binary_packet($packet); |
3010 | $this->send_binary_packet($packet); |
Line 2753... | Line 3012... | ||
2753 | $response = $this->get_channel_packet(self::CHANNEL_SHELL); |
3012 | $response = $this->get_channel_packet(self::CHANNEL_SHELL); |
2754 | if ($response === false) { |
3013 | if ($response === false) { |
2755 | throw new \RuntimeException('Unable to request shell'); |
3014 | throw new \RuntimeException('Unable to request shell'); |
2756 | } |
3015 | } |
2757 | 3016 | ||
2758 | $this->channel_status[self::CHANNEL_SHELL] = MessageType::CHANNEL_DATA; |
3017 | $this->channel_status[self::CHANNEL_SHELL] = NET_SSH2_MSG_CHANNEL_DATA; |
2759 | 3018 | ||
2760 | $this->bitmap |= self::MASK_SHELL; |
3019 | $this->bitmap |= self::MASK_SHELL; |
2761 | 3020 | ||
2762 | return true; |
3021 | return true; |
2763 | } |
3022 | } |
Line 2766... | Line 3025... | ||
2766 | * Return the channel to be used with read() / write() |
3025 | * Return the channel to be used with read() / write() |
2767 | * |
3026 | * |
2768 | * @see self::read() |
3027 | * @see self::read() |
2769 | * @see self::write() |
3028 | * @see self::write() |
2770 | * @return int |
3029 | * @return int |
- | 3030 | * @access public |
|
2771 | */ |
3031 | */ |
2772 | private function get_interactive_channel() |
3032 | private function get_interactive_channel() |
2773 | { |
3033 | { |
2774 | switch (true) { |
3034 | switch (true) { |
2775 | case $this->in_subsystem: |
3035 | case $this->in_subsystem: |
Line 2783... | Line 3043... | ||
2783 | 3043 | ||
2784 | /** |
3044 | /** |
2785 | * Return an available open channel |
3045 | * Return an available open channel |
2786 | * |
3046 | * |
2787 | * @return int |
3047 | * @return int |
- | 3048 | * @access public |
|
2788 | */ |
3049 | */ |
2789 | private function get_open_channel() |
3050 | private function get_open_channel() |
2790 | { |
3051 | { |
2791 | $channel = self::CHANNEL_EXEC; |
3052 | $channel = self::CHANNEL_EXEC; |
2792 | do { |
3053 | do { |
2793 | if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == MessageType::CHANNEL_OPEN) { |
3054 | if (isset($this->channel_status[$channel]) && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_OPEN) { |
2794 | return $channel; |
3055 | return $channel; |
2795 | } |
3056 | } |
2796 | } while ($channel++ < self::CHANNEL_SUBSYSTEM); |
3057 | } while ($channel++ < self::CHANNEL_SUBSYSTEM); |
2797 | 3058 | ||
2798 | return false; |
3059 | return false; |
Line 2800... | Line 3061... | ||
2800 | 3061 | ||
2801 | /** |
3062 | /** |
2802 | * Request agent forwarding of remote server |
3063 | * Request agent forwarding of remote server |
2803 | * |
3064 | * |
2804 | * @return bool |
3065 | * @return bool |
- | 3066 | * @access public |
|
2805 | */ |
3067 | */ |
2806 | public function requestAgentForwarding() |
3068 | public function requestAgentForwarding() |
2807 | { |
3069 | { |
2808 | $request_channel = $this->get_open_channel(); |
3070 | $request_channel = $this->get_open_channel(); |
2809 | if ($request_channel === false) { |
3071 | if ($request_channel === false) { |
2810 | return false; |
3072 | return false; |
2811 | } |
3073 | } |
2812 | 3074 | ||
2813 | $packet = Strings::packSSH2( |
3075 | $packet = Strings::packSSH2( |
2814 | 'CNsC', |
3076 | 'CNsC', |
2815 | MessageType::CHANNEL_REQUEST, |
3077 | NET_SSH2_MSG_CHANNEL_REQUEST, |
2816 | $this->server_channels[$request_channel], |
3078 | $this->server_channels[$request_channel], |
2817 | 'auth-agent-req@openssh.com', |
3079 | 'auth-agent-req@openssh.com', |
2818 | 1 |
3080 | 1 |
2819 | ); |
3081 | ); |
2820 | 3082 | ||
2821 | $this->channel_status[$request_channel] = MessageType::CHANNEL_REQUEST; |
3083 | $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_REQUEST; |
2822 | 3084 | ||
2823 | $this->send_binary_packet($packet); |
3085 | $this->send_binary_packet($packet); |
2824 | 3086 | ||
2825 | if (!$this->get_channel_packet($request_channel)) { |
3087 | if (!$this->get_channel_packet($request_channel)) { |
2826 | return false; |
3088 | return false; |
2827 | } |
3089 | } |
2828 | 3090 | ||
2829 | $this->channel_status[$request_channel] = MessageType::CHANNEL_OPEN; |
3091 | $this->channel_status[$request_channel] = NET_SSH2_MSG_CHANNEL_OPEN; |
2830 | 3092 | ||
2831 | return true; |
3093 | return true; |
2832 | } |
3094 | } |
2833 | 3095 | ||
2834 | /** |
3096 | /** |
Line 2840... | Line 3102... | ||
2840 | * @see self::write() |
3102 | * @see self::write() |
2841 | * @param string $expect |
3103 | * @param string $expect |
2842 | * @param int $mode |
3104 | * @param int $mode |
2843 | * @return string|bool|null |
3105 | * @return string|bool|null |
2844 | * @throws \RuntimeException on connection error |
3106 | * @throws \RuntimeException on connection error |
- | 3107 | * @access public |
|
2845 | */ |
3108 | */ |
2846 | public function read($expect = '', $mode = self::READ_SIMPLE) |
3109 | public function read($expect = '', $mode = self::READ_SIMPLE) |
2847 | { |
3110 | { |
2848 | $this->curTimeout = $this->timeout; |
3111 | $this->curTimeout = $this->timeout; |
2849 | $this->is_timeout = false; |
3112 | $this->is_timeout = false; |
Line 2913... | Line 3176... | ||
2913 | * if there's sufficient demand for such a feature. |
3176 | * if there's sufficient demand for such a feature. |
2914 | * |
3177 | * |
2915 | * @see self::stopSubsystem() |
3178 | * @see self::stopSubsystem() |
2916 | * @param string $subsystem |
3179 | * @param string $subsystem |
2917 | * @return bool |
3180 | * @return bool |
- | 3181 | * @access public |
|
2918 | */ |
3182 | */ |
2919 | public function startSubsystem($subsystem) |
3183 | public function startSubsystem($subsystem) |
2920 | { |
3184 | { |
2921 | $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; |
3185 | $this->window_size_server_to_client[self::CHANNEL_SUBSYSTEM] = $this->window_size; |
2922 | 3186 | ||
2923 | $packet = Strings::packSSH2( |
3187 | $packet = Strings::packSSH2( |
2924 | 'CsN3', |
3188 | 'CsN3', |
2925 | MessageType::CHANNEL_OPEN, |
3189 | NET_SSH2_MSG_CHANNEL_OPEN, |
2926 | 'session', |
3190 | 'session', |
2927 | self::CHANNEL_SUBSYSTEM, |
3191 | self::CHANNEL_SUBSYSTEM, |
2928 | $this->window_size, |
3192 | $this->window_size, |
2929 | 0x4000 |
3193 | 0x4000 |
2930 | ); |
3194 | ); |
2931 | 3195 | ||
2932 | $this->send_binary_packet($packet); |
3196 | $this->send_binary_packet($packet); |
2933 | 3197 | ||
2934 | $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_OPEN; |
3198 | $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_OPEN; |
2935 | 3199 | ||
2936 | $this->get_channel_packet(self::CHANNEL_SUBSYSTEM); |
3200 | $this->get_channel_packet(self::CHANNEL_SUBSYSTEM); |
2937 | 3201 | ||
2938 | $packet = Strings::packSSH2( |
3202 | $packet = Strings::packSSH2( |
2939 | 'CNsCs', |
3203 | 'CNsCs', |
2940 | MessageType::CHANNEL_REQUEST, |
3204 | NET_SSH2_MSG_CHANNEL_REQUEST, |
2941 | $this->server_channels[self::CHANNEL_SUBSYSTEM], |
3205 | $this->server_channels[self::CHANNEL_SUBSYSTEM], |
2942 | 'subsystem', |
3206 | 'subsystem', |
2943 | 1, |
3207 | 1, |
2944 | $subsystem |
3208 | $subsystem |
2945 | ); |
3209 | ); |
2946 | $this->send_binary_packet($packet); |
3210 | $this->send_binary_packet($packet); |
2947 | 3211 | ||
2948 | $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_REQUEST; |
3212 | $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_REQUEST; |
2949 | 3213 | ||
2950 | if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) { |
3214 | if (!$this->get_channel_packet(self::CHANNEL_SUBSYSTEM)) { |
2951 | return false; |
3215 | return false; |
2952 | } |
3216 | } |
2953 | 3217 | ||
2954 | $this->channel_status[self::CHANNEL_SUBSYSTEM] = MessageType::CHANNEL_DATA; |
3218 | $this->channel_status[self::CHANNEL_SUBSYSTEM] = NET_SSH2_MSG_CHANNEL_DATA; |
2955 | 3219 | ||
2956 | $this->bitmap |= self::MASK_SHELL; |
3220 | $this->bitmap |= self::MASK_SHELL; |
2957 | $this->in_subsystem = true; |
3221 | $this->in_subsystem = true; |
2958 | 3222 | ||
2959 | return true; |
3223 | return true; |
Line 2962... | Line 3226... | ||
2962 | /** |
3226 | /** |
2963 | * Stops a subsystem. |
3227 | * Stops a subsystem. |
2964 | * |
3228 | * |
2965 | * @see self::startSubsystem() |
3229 | * @see self::startSubsystem() |
2966 | * @return bool |
3230 | * @return bool |
- | 3231 | * @access public |
|
2967 | */ |
3232 | */ |
2968 | public function stopSubsystem() |
3233 | public function stopSubsystem() |
2969 | { |
3234 | { |
2970 | $this->in_subsystem = false; |
3235 | $this->in_subsystem = false; |
2971 | $this->close_channel(self::CHANNEL_SUBSYSTEM); |
3236 | $this->close_channel(self::CHANNEL_SUBSYSTEM); |
Line 2975... | Line 3240... | ||
2975 | /** |
3240 | /** |
2976 | * Closes a channel |
3241 | * Closes a channel |
2977 | * |
3242 | * |
2978 | * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call |
3243 | * If read() timed out you might want to just close the channel and have it auto-restart on the next read() call |
2979 | * |
3244 | * |
- | 3245 | * @access public |
|
2980 | */ |
3246 | */ |
2981 | public function reset() |
3247 | public function reset() |
2982 | { |
3248 | { |
2983 | $this->close_channel($this->get_interactive_channel()); |
3249 | $this->close_channel($this->get_interactive_channel()); |
2984 | } |
3250 | } |
Line 2986... | Line 3252... | ||
2986 | /** |
3252 | /** |
2987 | * Is timeout? |
3253 | * Is timeout? |
2988 | * |
3254 | * |
2989 | * Did exec() or read() return because they timed out or because they encountered the end? |
3255 | * Did exec() or read() return because they timed out or because they encountered the end? |
2990 | * |
3256 | * |
- | 3257 | * @access public |
|
2991 | */ |
3258 | */ |
2992 | public function isTimeout() |
3259 | public function isTimeout() |
2993 | { |
3260 | { |
2994 | return $this->is_timeout; |
3261 | return $this->is_timeout; |
2995 | } |
3262 | } |
2996 | 3263 | ||
2997 | /** |
3264 | /** |
2998 | * Disconnect |
3265 | * Disconnect |
2999 | * |
3266 | * |
- | 3267 | * @access public |
|
3000 | */ |
3268 | */ |
3001 | public function disconnect() |
3269 | public function disconnect() |
3002 | { |
3270 | { |
3003 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
3271 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
3004 | if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { |
3272 | if (isset($this->realtime_log_file) && is_resource($this->realtime_log_file)) { |
3005 | fclose($this->realtime_log_file); |
3273 | fclose($this->realtime_log_file); |
3006 | } |
3274 | } |
3007 | unset(self::$connections[$this->getResourceId()]); |
3275 | unset(self::$connections[$this->getResourceId()]); |
3008 | } |
3276 | } |
Line 3011... | Line 3279... | ||
3011 | * Destructor. |
3279 | * Destructor. |
3012 | * |
3280 | * |
3013 | * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call |
3281 | * Will be called, automatically, if you're supporting just PHP5. If you're supporting PHP4, you'll need to call |
3014 | * disconnect(). |
3282 | * disconnect(). |
3015 | * |
3283 | * |
- | 3284 | * @access public |
|
3016 | */ |
3285 | */ |
3017 | public function __destruct() |
3286 | public function __destruct() |
3018 | { |
3287 | { |
3019 | $this->disconnect(); |
3288 | $this->disconnect(); |
3020 | } |
3289 | } |
3021 | 3290 | ||
3022 | /** |
3291 | /** |
3023 | * Is the connection still active? |
3292 | * Is the connection still active? |
3024 | * |
3293 | * |
3025 | * @return bool |
3294 | * @return bool |
- | 3295 | * @access public |
|
3026 | */ |
3296 | */ |
3027 | public function isConnected() |
3297 | public function isConnected() |
3028 | { |
3298 | { |
3029 | return (bool) ($this->bitmap & self::MASK_CONNECTED); |
3299 | return (bool) ($this->bitmap & self::MASK_CONNECTED); |
3030 | } |
3300 | } |
3031 | 3301 | ||
3032 | /** |
3302 | /** |
3033 | * Have you successfully been logged in? |
3303 | * Have you successfully been logged in? |
3034 | * |
3304 | * |
3035 | * @return bool |
3305 | * @return bool |
- | 3306 | * @access public |
|
3036 | */ |
3307 | */ |
3037 | public function isAuthenticated() |
3308 | public function isAuthenticated() |
3038 | { |
3309 | { |
3039 | return (bool) ($this->bitmap & self::MASK_LOGIN); |
3310 | return (bool) ($this->bitmap & self::MASK_LOGIN); |
3040 | } |
3311 | } |
Line 3057... | Line 3328... | ||
3057 | 3328 | ||
3058 | $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size; |
3329 | $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE] = $this->window_size; |
3059 | $packet_size = 0x4000; |
3330 | $packet_size = 0x4000; |
3060 | $packet = Strings::packSSH2( |
3331 | $packet = Strings::packSSH2( |
3061 | 'CsN3', |
3332 | 'CsN3', |
3062 | MessageType::CHANNEL_OPEN, |
3333 | NET_SSH2_MSG_CHANNEL_OPEN, |
3063 | 'session', |
3334 | 'session', |
3064 | self::CHANNEL_KEEP_ALIVE, |
3335 | self::CHANNEL_KEEP_ALIVE, |
3065 | $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE], |
3336 | $this->window_size_server_to_client[self::CHANNEL_KEEP_ALIVE], |
3066 | $packet_size |
3337 | $packet_size |
3067 | ); |
3338 | ); |
3068 | 3339 | ||
3069 | try { |
3340 | try { |
3070 | $this->send_binary_packet($packet); |
3341 | $this->send_binary_packet($packet); |
3071 | 3342 | ||
3072 | $this->channel_status[self::CHANNEL_KEEP_ALIVE] = MessageType::CHANNEL_OPEN; |
3343 | $this->channel_status[self::CHANNEL_KEEP_ALIVE] = NET_SSH2_MSG_CHANNEL_OPEN; |
3073 | 3344 | ||
3074 | $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE); |
3345 | $response = $this->get_channel_packet(self::CHANNEL_KEEP_ALIVE); |
3075 | } catch (\RuntimeException $e) { |
3346 | } catch (\RuntimeException $e) { |
3076 | return $this->reconnect(); |
3347 | return $this->reconnect(); |
3077 | } |
3348 | } |
Line 3085... | Line 3356... | ||
3085 | * |
3356 | * |
3086 | * @return boolean |
3357 | * @return boolean |
3087 | */ |
3358 | */ |
3088 | private function reconnect() |
3359 | private function reconnect() |
3089 | { |
3360 | { |
3090 | $this->reset_connection(DisconnectReason::CONNECTION_LOST); |
3361 | $this->reset_connection(NET_SSH2_DISCONNECT_CONNECTION_LOST); |
3091 | $this->retry_connect = true; |
3362 | $this->retry_connect = true; |
3092 | $this->connect(); |
3363 | $this->connect(); |
3093 | foreach ($this->auth as $auth) { |
3364 | foreach ($this->auth as $auth) { |
3094 | $result = $this->login(...$auth); |
3365 | $result = $this->login(...$auth); |
3095 | } |
3366 | } |
Line 3098... | Line 3369... | ||
3098 | 3369 | ||
3099 | /** |
3370 | /** |
3100 | * Resets a connection for re-use |
3371 | * Resets a connection for re-use |
3101 | * |
3372 | * |
3102 | * @param int $reason |
3373 | * @param int $reason |
- | 3374 | * @access private |
|
3103 | */ |
3375 | */ |
3104 | protected function reset_connection($reason) |
3376 | protected function reset_connection($reason) |
3105 | { |
3377 | { |
3106 | $this->disconnect_helper($reason); |
3378 | $this->disconnect_helper($reason); |
3107 | $this->decrypt = $this->encrypt = false; |
3379 | $this->decrypt = $this->encrypt = false; |
Line 3119... | Line 3391... | ||
3119 | * See '6. Binary Packet Protocol' of rfc4253 for more info. |
3391 | * See '6. Binary Packet Protocol' of rfc4253 for more info. |
3120 | * |
3392 | * |
3121 | * @see self::_send_binary_packet() |
3393 | * @see self::_send_binary_packet() |
3122 | * @param bool $skip_channel_filter |
3394 | * @param bool $skip_channel_filter |
3123 | * @return bool|string |
3395 | * @return bool|string |
- | 3396 | * @access private |
|
3124 | */ |
3397 | */ |
3125 | private function get_binary_packet($skip_channel_filter = false) |
3398 | private function get_binary_packet($skip_channel_filter = false) |
3126 | { |
3399 | { |
3127 | if ($skip_channel_filter) { |
3400 | if ($skip_channel_filter) { |
3128 | if (!is_resource($this->fsock)) { |
3401 | if (!is_resource($this->fsock)) { |
Line 3134... | Line 3407... | ||
3134 | if (!$this->curTimeout) { |
3407 | if (!$this->curTimeout) { |
3135 | if ($this->keepAlive <= 0) { |
3408 | if ($this->keepAlive <= 0) { |
3136 | @stream_select($read, $write, $except, null); |
3409 | @stream_select($read, $write, $except, null); |
3137 | } else { |
3410 | } else { |
3138 | if (!@stream_select($read, $write, $except, $this->keepAlive)) { |
3411 | if (!@stream_select($read, $write, $except, $this->keepAlive)) { |
3139 | $this->send_binary_packet(pack('CN', MessageType::IGNORE, 0)); |
3412 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); |
3140 | return $this->get_binary_packet(true); |
3413 | return $this->get_binary_packet(true); |
3141 | } |
3414 | } |
3142 | } |
3415 | } |
3143 | } else { |
3416 | } else { |
3144 | if ($this->curTimeout < 0) { |
3417 | if ($this->curTimeout < 0) { |
Line 3148... | Line 3421... | ||
3148 | 3421 | ||
3149 | $start = microtime(true); |
3422 | $start = microtime(true); |
3150 | 3423 | ||
3151 | if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { |
3424 | if ($this->keepAlive > 0 && $this->keepAlive < $this->curTimeout) { |
3152 | if (!@stream_select($read, $write, $except, $this->keepAlive)) { |
3425 | if (!@stream_select($read, $write, $except, $this->keepAlive)) { |
3153 | $this->send_binary_packet(pack('CN', MessageType::IGNORE, 0)); |
3426 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_IGNORE, 0)); |
3154 | $elapsed = microtime(true) - $start; |
3427 | $elapsed = microtime(true) - $start; |
3155 | $this->curTimeout -= $elapsed; |
3428 | $this->curTimeout -= $elapsed; |
3156 | return $this->get_binary_packet(true); |
3429 | return $this->get_binary_packet(true); |
3157 | } |
3430 | } |
3158 | $elapsed = microtime(true) - $start; |
3431 | $elapsed = microtime(true) - $start; |
Line 3286... | Line 3559... | ||
3286 | $padding = Strings::shift($raw, $padding_length); // should leave $raw empty |
3559 | $padding = Strings::shift($raw, $padding_length); // should leave $raw empty |
3287 | 3560 | ||
3288 | if ($this->hmac_check instanceof Hash) { |
3561 | if ($this->hmac_check instanceof Hash) { |
3289 | $hmac = stream_get_contents($this->fsock, $this->hmac_size); |
3562 | $hmac = stream_get_contents($this->fsock, $this->hmac_size); |
3290 | if ($hmac === false || strlen($hmac) != $this->hmac_size) { |
3563 | if ($hmac === false || strlen($hmac) != $this->hmac_size) { |
3291 | $this->disconnect_helper(DisconnectReason::MAC_ERROR); |
3564 | $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); |
3292 | throw new \RuntimeException('Error reading socket'); |
3565 | throw new \RuntimeException('Error reading socket'); |
3293 | } |
3566 | } |
3294 | 3567 | ||
3295 | $reconstructed = !$this->hmac_check_etm ? |
3568 | $reconstructed = !$this->hmac_check_etm ? |
3296 | pack('NCa*', $packet_length, $padding_length, $payload . $padding) : |
3569 | pack('NCa*', $packet_length, $padding_length, $payload . $padding) : |
3297 | $encrypted; |
3570 | $encrypted; |
3298 | if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') { |
3571 | if (($this->hmac_check->getHash() & "\xFF\xFF\xFF\xFF") == 'umac') { |
3299 | $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no)); |
3572 | $this->hmac_check->setNonce("\0\0\0\0" . pack('N', $this->get_seq_no)); |
3300 | if ($hmac != $this->hmac_check->hash($reconstructed)) { |
3573 | if ($hmac != $this->hmac_check->hash($reconstructed)) { |
3301 | $this->disconnect_helper(DisconnectReason::MAC_ERROR); |
3574 | $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); |
3302 | throw new \RuntimeException('Invalid UMAC'); |
3575 | throw new \RuntimeException('Invalid UMAC'); |
3303 | } |
3576 | } |
3304 | } else { |
3577 | } else { |
3305 | if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) { |
3578 | if ($hmac != $this->hmac_check->hash(pack('Na*', $this->get_seq_no, $reconstructed))) { |
3306 | $this->disconnect_helper(DisconnectReason::MAC_ERROR); |
3579 | $this->disconnect_helper(NET_SSH2_DISCONNECT_MAC_ERROR); |
3307 | throw new \RuntimeException('Invalid HMAC'); |
3580 | throw new \RuntimeException('Invalid HMAC'); |
3308 | } |
3581 | } |
3309 | } |
3582 | } |
3310 | } |
3583 | } |
3311 | 3584 | ||
Line 3348... | Line 3621... | ||
3348 | 3621 | ||
3349 | $this->get_seq_no++; |
3622 | $this->get_seq_no++; |
3350 | 3623 | ||
3351 | if (defined('NET_SSH2_LOGGING')) { |
3624 | if (defined('NET_SSH2_LOGGING')) { |
3352 | $current = microtime(true); |
3625 | $current = microtime(true); |
3353 | $message_number = sprintf( |
- | |
3354 | '<- %s (since last: %s, network: %ss)', |
- | |
3355 | ($constantName = MessageType::findConstantNameByValue($value = ord($payload[0]))) |
3626 | $message_number = isset($this->message_numbers[ord($payload[0])]) ? $this->message_numbers[ord($payload[0])] : 'UNKNOWN (' . ord($payload[0]) . ')'; |
3356 | ? "SSH_MSG_$constantName" |
3627 | $message_number = '<- ' . $message_number . |
3357 | : "UNKNOWN ($value)", |
- | |
3358 | round($current - $this->last_packet, 4), |
3628 | ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; |
3359 | round($stop - $start, 4) |
- | |
3360 | ); |
- | |
3361 | $this->append_log($message_number, $payload); |
3629 | $this->append_log($message_number, $payload); |
3362 | $this->last_packet = $current; |
3630 | $this->last_packet = $current; |
3363 | } |
3631 | } |
3364 | 3632 | ||
3365 | return $this->filter($payload, $skip_channel_filter); |
3633 | return $this->filter($payload, $skip_channel_filter); |
Line 3369... | Line 3637... | ||
3369 | * Read Remaining Bytes |
3637 | * Read Remaining Bytes |
3370 | * |
3638 | * |
3371 | * @see self::get_binary_packet() |
3639 | * @see self::get_binary_packet() |
3372 | * @param int $remaining_length |
3640 | * @param int $remaining_length |
3373 | * @return string |
3641 | * @return string |
- | 3642 | * @access private |
|
3374 | */ |
3643 | */ |
3375 | private function read_remaining_bytes($remaining_length) |
3644 | private function read_remaining_bytes($remaining_length) |
3376 | { |
3645 | { |
3377 | if (!$remaining_length) { |
3646 | if (!$remaining_length) { |
3378 | return ''; |
3647 | return ''; |
Line 3395... | Line 3664... | ||
3395 | // PuTTY uses 0x9000 as the actual max packet size and so to shall we |
3664 | // PuTTY uses 0x9000 as the actual max packet size and so to shall we |
3396 | // don't do this when GCM mode is used since GCM mode doesn't encrypt the length |
3665 | // don't do this when GCM mode is used since GCM mode doesn't encrypt the length |
3397 | if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { |
3666 | if ($remaining_length < -$this->decrypt_block_size || $remaining_length > 0x9000 || $remaining_length % $this->decrypt_block_size != 0) { |
3398 | if (!$this->bad_key_size_fix && self::bad_algorithm_candidate($this->decrypt ? $this->decryptName : '') && !($this->bitmap & SSH2::MASK_LOGIN)) { |
3667 | if (!$this->bad_key_size_fix && self::bad_algorithm_candidate($this->decrypt ? $this->decryptName : '') && !($this->bitmap & SSH2::MASK_LOGIN)) { |
3399 | $this->bad_key_size_fix = true; |
3668 | $this->bad_key_size_fix = true; |
3400 | $this->reset_connection(DisconnectReason::KEY_EXCHANGE_FAILED); |
3669 | $this->reset_connection(NET_SSH2_DISCONNECT_KEY_EXCHANGE_FAILED); |
3401 | return false; |
3670 | return false; |
3402 | } |
3671 | } |
3403 | throw new \RuntimeException('Invalid size'); |
3672 | throw new \RuntimeException('Invalid size'); |
3404 | } |
3673 | } |
3405 | 3674 | ||
Line 3409... | Line 3678... | ||
3409 | 3678 | ||
3410 | $buffer = ''; |
3679 | $buffer = ''; |
3411 | while ($remaining_length > 0) { |
3680 | while ($remaining_length > 0) { |
3412 | $temp = stream_get_contents($this->fsock, $remaining_length); |
3681 | $temp = stream_get_contents($this->fsock, $remaining_length); |
3413 | if ($temp === false || feof($this->fsock)) { |
3682 | if ($temp === false || feof($this->fsock)) { |
3414 | $this->disconnect_helper(DisconnectReason::CONNECTION_LOST); |
3683 | $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); |
3415 | throw new \RuntimeException('Error reading from socket'); |
3684 | throw new \RuntimeException('Error reading from socket'); |
3416 | } |
3685 | } |
3417 | $buffer .= $temp; |
3686 | $buffer .= $temp; |
3418 | $remaining_length -= strlen($temp); |
3687 | $remaining_length -= strlen($temp); |
3419 | } |
3688 | } |
Line 3428... | Line 3697... | ||
3428 | * |
3697 | * |
3429 | * @see self::_get_binary_packet() |
3698 | * @see self::_get_binary_packet() |
3430 | * @param string $payload |
3699 | * @param string $payload |
3431 | * @param bool $skip_channel_filter |
3700 | * @param bool $skip_channel_filter |
3432 | * @return string|bool |
3701 | * @return string|bool |
- | 3702 | * @access private |
|
3433 | */ |
3703 | */ |
3434 | private function filter($payload, $skip_channel_filter) |
3704 | private function filter($payload, $skip_channel_filter) |
3435 | { |
3705 | { |
3436 | switch (ord($payload[0])) { |
3706 | switch (ord($payload[0])) { |
3437 | case MessageType::DISCONNECT: |
3707 | case NET_SSH2_MSG_DISCONNECT: |
3438 | Strings::shift($payload, 1); |
3708 | Strings::shift($payload, 1); |
3439 | list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload); |
3709 | list($reason_code, $message) = Strings::unpackSSH2('Ns', $payload); |
3440 | $this->errors[] = 'SSH_MSG_DISCONNECT: SSH_DISCONNECT_' . DisconnectReason::getConstantNameByValue($reason_code) . "\r\n$message"; |
3710 | $this->errors[] = 'SSH_MSG_DISCONNECT: ' . $this->disconnect_reasons[$reason_code] . "\r\n$message"; |
3441 | $this->bitmap = 0; |
3711 | $this->bitmap = 0; |
3442 | return false; |
3712 | return false; |
3443 | case MessageType::IGNORE: |
3713 | case NET_SSH2_MSG_IGNORE: |
3444 | $payload = $this->get_binary_packet($skip_channel_filter); |
3714 | $payload = $this->get_binary_packet($skip_channel_filter); |
3445 | break; |
3715 | break; |
3446 | case MessageType::DEBUG: |
3716 | case NET_SSH2_MSG_DEBUG: |
3447 | Strings::shift($payload, 2); // second byte is "always_display" |
3717 | Strings::shift($payload, 2); // second byte is "always_display" |
3448 | list($message) = Strings::unpackSSH2('s', $payload); |
3718 | list($message) = Strings::unpackSSH2('s', $payload); |
3449 | $this->errors[] = "SSH_MSG_DEBUG: $message"; |
3719 | $this->errors[] = "SSH_MSG_DEBUG: $message"; |
3450 | $payload = $this->get_binary_packet($skip_channel_filter); |
3720 | $payload = $this->get_binary_packet($skip_channel_filter); |
3451 | break; |
3721 | break; |
3452 | case MessageType::UNIMPLEMENTED: |
3722 | case NET_SSH2_MSG_UNIMPLEMENTED: |
3453 | return false; |
3723 | return false; |
3454 | case MessageType::KEXINIT: |
3724 | case NET_SSH2_MSG_KEXINIT: |
3455 | if ($this->session_id !== false) { |
3725 | if ($this->session_id !== false) { |
3456 | if (!$this->key_exchange($payload)) { |
3726 | if (!$this->key_exchange($payload)) { |
3457 | $this->bitmap = 0; |
3727 | $this->bitmap = 0; |
3458 | return false; |
3728 | return false; |
3459 | } |
3729 | } |
3460 | $payload = $this->get_binary_packet($skip_channel_filter); |
3730 | $payload = $this->get_binary_packet($skip_channel_filter); |
3461 | } |
3731 | } |
3462 | } |
3732 | } |
3463 | 3733 | ||
3464 | // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in |
3734 | // see http://tools.ietf.org/html/rfc4252#section-5.4; only called when the encryption has been activated and when we haven't already logged in |
3465 | if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !is_bool($payload) && ord($payload[0]) == MessageType::USERAUTH_BANNER) { |
3735 | if (($this->bitmap & self::MASK_CONNECTED) && !$this->isAuthenticated() && !is_bool($payload) && ord($payload[0]) == NET_SSH2_MSG_USERAUTH_BANNER) { |
3466 | Strings::shift($payload, 1); |
3736 | Strings::shift($payload, 1); |
3467 | list($this->banner_message) = Strings::unpackSSH2('s', $payload); |
3737 | list($this->banner_message) = Strings::unpackSSH2('s', $payload); |
3468 | $payload = $this->get_binary_packet(); |
3738 | $payload = $this->get_binary_packet(); |
3469 | } |
3739 | } |
3470 | 3740 | ||
Line 3473... | Line 3743... | ||
3473 | if (is_bool($payload)) { |
3743 | if (is_bool($payload)) { |
3474 | return $payload; |
3744 | return $payload; |
3475 | } |
3745 | } |
3476 | 3746 | ||
3477 | switch (ord($payload[0])) { |
3747 | switch (ord($payload[0])) { |
3478 | case MessageType::CHANNEL_REQUEST: |
3748 | case NET_SSH2_MSG_CHANNEL_REQUEST: |
3479 | if (strlen($payload) == 31) { |
3749 | if (strlen($payload) == 31) { |
3480 | extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); |
3750 | extract(unpack('cpacket_type/Nchannel/Nlength', $payload)); |
3481 | if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { |
3751 | if (substr($payload, 9, $length) == 'keepalive@openssh.com' && isset($this->server_channels[$channel])) { |
3482 | if (ord(substr($payload, 9 + $length))) { // want reply |
3752 | if (ord(substr($payload, 9 + $length))) { // want reply |
3483 | $this->send_binary_packet(pack('CN', MessageType::CHANNEL_SUCCESS, $this->server_channels[$channel])); |
3753 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_SUCCESS, $this->server_channels[$channel])); |
3484 | } |
3754 | } |
3485 | $payload = $this->get_binary_packet($skip_channel_filter); |
3755 | $payload = $this->get_binary_packet($skip_channel_filter); |
3486 | } |
3756 | } |
3487 | } |
3757 | } |
3488 | break; |
3758 | break; |
3489 | case MessageType::CHANNEL_DATA: |
3759 | case NET_SSH2_MSG_CHANNEL_DATA: |
3490 | case MessageType::CHANNEL_EXTENDED_DATA: |
3760 | case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: |
3491 | case MessageType::CHANNEL_CLOSE: |
3761 | case NET_SSH2_MSG_CHANNEL_CLOSE: |
3492 | case MessageType::CHANNEL_EOF: |
3762 | case NET_SSH2_MSG_CHANNEL_EOF: |
3493 | if (!$skip_channel_filter && !empty($this->server_channels)) { |
3763 | if (!$skip_channel_filter && !empty($this->server_channels)) { |
3494 | $this->binary_packet_buffer = $payload; |
3764 | $this->binary_packet_buffer = $payload; |
3495 | $this->get_channel_packet(true); |
3765 | $this->get_channel_packet(true); |
3496 | $payload = $this->get_binary_packet(); |
3766 | $payload = $this->get_binary_packet(); |
3497 | } |
3767 | } |
3498 | break; |
3768 | break; |
3499 | case MessageType::GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 |
3769 | case NET_SSH2_MSG_GLOBAL_REQUEST: // see http://tools.ietf.org/html/rfc4254#section-4 |
3500 | Strings::shift($payload, 1); |
3770 | Strings::shift($payload, 1); |
3501 | list($request_name) = Strings::unpackSSH2('s', $payload); |
3771 | list($request_name) = Strings::unpackSSH2('s', $payload); |
3502 | $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name"; |
3772 | $this->errors[] = "SSH_MSG_GLOBAL_REQUEST: $request_name"; |
3503 | 3773 | ||
3504 | try { |
3774 | try { |
3505 | $this->send_binary_packet(pack('C', MessageType::REQUEST_FAILURE)); |
3775 | $this->send_binary_packet(pack('C', NET_SSH2_MSG_REQUEST_FAILURE)); |
3506 | } catch (\RuntimeException $e) { |
3776 | } catch (\RuntimeException $e) { |
3507 | return $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
3777 | return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
3508 | } |
3778 | } |
3509 | 3779 | ||
3510 | $payload = $this->get_binary_packet($skip_channel_filter); |
3780 | $payload = $this->get_binary_packet($skip_channel_filter); |
3511 | break; |
3781 | break; |
3512 | case MessageType::CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 |
3782 | case NET_SSH2_MSG_CHANNEL_OPEN: // see http://tools.ietf.org/html/rfc4254#section-5.1 |
3513 | Strings::shift($payload, 1); |
3783 | Strings::shift($payload, 1); |
3514 | list($data, $server_channel) = Strings::unpackSSH2('sN', $payload); |
3784 | list($data, $server_channel) = Strings::unpackSSH2('sN', $payload); |
3515 | switch ($data) { |
3785 | switch ($data) { |
3516 | case 'auth-agent': |
3786 | case 'auth-agent': |
3517 | case 'auth-agent@openssh.com': |
3787 | case 'auth-agent@openssh.com': |
Line 3529... | Line 3799... | ||
3529 | 3799 | ||
3530 | $packet_size = 0x4000; |
3800 | $packet_size = 0x4000; |
3531 | 3801 | ||
3532 | $packet = pack( |
3802 | $packet = pack( |
3533 | 'CN4', |
3803 | 'CN4', |
3534 | MessageType::CHANNEL_OPEN_CONFIRMATION, |
3804 | NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION, |
3535 | $server_channel, |
3805 | $server_channel, |
3536 | $new_channel, |
3806 | $new_channel, |
3537 | $packet_size, |
3807 | $packet_size, |
3538 | $packet_size |
3808 | $packet_size |
3539 | ); |
3809 | ); |
3540 | 3810 | ||
3541 | $this->server_channels[$new_channel] = $server_channel; |
3811 | $this->server_channels[$new_channel] = $server_channel; |
3542 | $this->channel_status[$new_channel] = MessageType::CHANNEL_OPEN_CONFIRMATION; |
3812 | $this->channel_status[$new_channel] = NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION; |
3543 | $this->send_binary_packet($packet); |
3813 | $this->send_binary_packet($packet); |
3544 | } |
3814 | } |
3545 | break; |
3815 | break; |
3546 | default: |
3816 | default: |
3547 | $packet = Strings::packSSH2( |
3817 | $packet = Strings::packSSH2( |
3548 | 'CN2ss', |
3818 | 'CN2ss', |
3549 | MessageType::CHANNEL_OPEN_FAILURE, |
3819 | NET_SSH2_MSG_CHANNEL_OPEN_FAILURE, |
3550 | $server_channel, |
3820 | $server_channel, |
3551 | ChannelConnectionFailureReason::ADMINISTRATIVELY_PROHIBITED, |
3821 | NET_SSH2_OPEN_ADMINISTRATIVELY_PROHIBITED, |
3552 | '', // description |
3822 | '', // description |
3553 | '' // language tag |
3823 | '' // language tag |
3554 | ); |
3824 | ); |
3555 | 3825 | ||
3556 | try { |
3826 | try { |
3557 | $this->send_binary_packet($packet); |
3827 | $this->send_binary_packet($packet); |
3558 | } catch (\RuntimeException $e) { |
3828 | } catch (\RuntimeException $e) { |
3559 | return $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
3829 | return $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
3560 | } |
3830 | } |
3561 | } |
3831 | } |
3562 | 3832 | ||
3563 | $payload = $this->get_binary_packet($skip_channel_filter); |
3833 | $payload = $this->get_binary_packet($skip_channel_filter); |
3564 | break; |
3834 | break; |
3565 | case MessageType::CHANNEL_WINDOW_ADJUST: |
3835 | case NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST: |
3566 | Strings::shift($payload, 1); |
3836 | Strings::shift($payload, 1); |
3567 | list($channel, $window_size) = Strings::unpackSSH2('NN', $payload); |
3837 | list($channel, $window_size) = Strings::unpackSSH2('NN', $payload); |
3568 | 3838 | ||
3569 | $this->window_size_client_to_server[$channel] += $window_size; |
3839 | $this->window_size_client_to_server[$channel] += $window_size; |
3570 | 3840 | ||
Line 3578... | Line 3848... | ||
3578 | /** |
3848 | /** |
3579 | * Enable Quiet Mode |
3849 | * Enable Quiet Mode |
3580 | * |
3850 | * |
3581 | * Suppress stderr from output |
3851 | * Suppress stderr from output |
3582 | * |
3852 | * |
- | 3853 | * @access public |
|
3583 | */ |
3854 | */ |
3584 | public function enableQuietMode() |
3855 | public function enableQuietMode() |
3585 | { |
3856 | { |
3586 | $this->quiet_mode = true; |
3857 | $this->quiet_mode = true; |
3587 | } |
3858 | } |
Line 3589... | Line 3860... | ||
3589 | /** |
3860 | /** |
3590 | * Disable Quiet Mode |
3861 | * Disable Quiet Mode |
3591 | * |
3862 | * |
3592 | * Show stderr in output |
3863 | * Show stderr in output |
3593 | * |
3864 | * |
- | 3865 | * @access public |
|
3594 | */ |
3866 | */ |
3595 | public function disableQuietMode() |
3867 | public function disableQuietMode() |
3596 | { |
3868 | { |
3597 | $this->quiet_mode = false; |
3869 | $this->quiet_mode = false; |
3598 | } |
3870 | } |
Line 3600... | Line 3872... | ||
3600 | /** |
3872 | /** |
3601 | * Returns whether Quiet Mode is enabled or not |
3873 | * Returns whether Quiet Mode is enabled or not |
3602 | * |
3874 | * |
3603 | * @see self::enableQuietMode() |
3875 | * @see self::enableQuietMode() |
3604 | * @see self::disableQuietMode() |
3876 | * @see self::disableQuietMode() |
- | 3877 | * @access public |
|
3605 | * @return bool |
3878 | * @return bool |
3606 | */ |
3879 | */ |
3607 | public function isQuietModeEnabled() |
3880 | public function isQuietModeEnabled() |
3608 | { |
3881 | { |
3609 | return $this->quiet_mode; |
3882 | return $this->quiet_mode; |
3610 | } |
3883 | } |
3611 | 3884 | ||
3612 | /** |
3885 | /** |
3613 | * Enable request-pty when using exec() |
3886 | * Enable request-pty when using exec() |
3614 | * |
3887 | * |
- | 3888 | * @access public |
|
3615 | */ |
3889 | */ |
3616 | public function enablePTY() |
3890 | public function enablePTY() |
3617 | { |
3891 | { |
3618 | $this->request_pty = true; |
3892 | $this->request_pty = true; |
3619 | } |
3893 | } |
3620 | 3894 | ||
3621 | /** |
3895 | /** |
3622 | * Disable request-pty when using exec() |
3896 | * Disable request-pty when using exec() |
3623 | * |
3897 | * |
- | 3898 | * @access public |
|
3624 | */ |
3899 | */ |
3625 | public function disablePTY() |
3900 | public function disablePTY() |
3626 | { |
3901 | { |
3627 | if ($this->in_request_pty_exec) { |
3902 | if ($this->in_request_pty_exec) { |
3628 | $this->close_channel(self::CHANNEL_EXEC); |
3903 | $this->close_channel(self::CHANNEL_EXEC); |
Line 3634... | Line 3909... | ||
3634 | /** |
3909 | /** |
3635 | * Returns whether request-pty is enabled or not |
3910 | * Returns whether request-pty is enabled or not |
3636 | * |
3911 | * |
3637 | * @see self::enablePTY() |
3912 | * @see self::enablePTY() |
3638 | * @see self::disablePTY() |
3913 | * @see self::disablePTY() |
- | 3914 | * @access public |
|
3639 | * @return bool |
3915 | * @return bool |
3640 | */ |
3916 | */ |
3641 | public function isPTYEnabled() |
3917 | public function isPTYEnabled() |
3642 | { |
3918 | { |
3643 | return $this->request_pty; |
3919 | return $this->request_pty; |
Line 3659... | Line 3935... | ||
3659 | * |
3935 | * |
3660 | * @param int $client_channel |
3936 | * @param int $client_channel |
3661 | * @param bool $skip_extended |
3937 | * @param bool $skip_extended |
3662 | * @return mixed |
3938 | * @return mixed |
3663 | * @throws \RuntimeException on connection error |
3939 | * @throws \RuntimeException on connection error |
- | 3940 | * @access private |
|
3664 | */ |
3941 | */ |
3665 | protected function get_channel_packet($client_channel, $skip_extended = false) |
3942 | protected function get_channel_packet($client_channel, $skip_extended = false) |
3666 | { |
3943 | { |
3667 | if (!empty($this->channel_buffers[$client_channel])) { |
3944 | if (!empty($this->channel_buffers[$client_channel])) { |
3668 | switch ($this->channel_status[$client_channel]) { |
3945 | switch ($this->channel_status[$client_channel]) { |
3669 | case MessageType::CHANNEL_REQUEST: |
3946 | case NET_SSH2_MSG_CHANNEL_REQUEST: |
3670 | foreach ($this->channel_buffers[$client_channel] as $i => $packet) { |
3947 | foreach ($this->channel_buffers[$client_channel] as $i => $packet) { |
3671 | switch (ord($packet[0])) { |
3948 | switch (ord($packet[0])) { |
3672 | case MessageType::CHANNEL_SUCCESS: |
3949 | case NET_SSH2_MSG_CHANNEL_SUCCESS: |
3673 | case MessageType::CHANNEL_FAILURE: |
3950 | case NET_SSH2_MSG_CHANNEL_FAILURE: |
3674 | unset($this->channel_buffers[$client_channel][$i]); |
3951 | unset($this->channel_buffers[$client_channel][$i]); |
3675 | return substr($packet, 1); |
3952 | return substr($packet, 1); |
3676 | } |
3953 | } |
3677 | } |
3954 | } |
3678 | break; |
3955 | break; |
Line 3692... | Line 3969... | ||
3692 | $this->close_channel($client_channel); |
3969 | $this->close_channel($client_channel); |
3693 | } |
3970 | } |
3694 | return true; |
3971 | return true; |
3695 | } |
3972 | } |
3696 | if ($response === false) { |
3973 | if ($response === false) { |
3697 | $this->disconnect_helper(DisconnectReason::CONNECTION_LOST); |
3974 | $this->disconnect_helper(NET_SSH2_DISCONNECT_CONNECTION_LOST); |
3698 | throw new ConnectionClosedException('Connection closed by server'); |
3975 | throw new ConnectionClosedException('Connection closed by server'); |
3699 | } |
3976 | } |
3700 | } |
3977 | } |
3701 | 3978 | ||
3702 | if ($client_channel == -1 && $response === true) { |
3979 | if ($client_channel == -1 && $response === true) { |
Line 3710... | Line 3987... | ||
3710 | 3987 | ||
3711 | // resize the window, if appropriate |
3988 | // resize the window, if appropriate |
3712 | if ($this->window_size_server_to_client[$channel] < 0) { |
3989 | if ($this->window_size_server_to_client[$channel] < 0) { |
3713 | // PuTTY does something more analogous to the following: |
3990 | // PuTTY does something more analogous to the following: |
3714 | //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) { |
3991 | //if ($this->window_size_server_to_client[$channel] < 0x3FFFFFFF) { |
3715 | $packet = pack('CNN', MessageType::CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize); |
3992 | $packet = pack('CNN', NET_SSH2_MSG_CHANNEL_WINDOW_ADJUST, $this->server_channels[$channel], $this->window_resize); |
3716 | $this->send_binary_packet($packet); |
3993 | $this->send_binary_packet($packet); |
3717 | $this->window_size_server_to_client[$channel] += $this->window_resize; |
3994 | $this->window_size_server_to_client[$channel] += $this->window_resize; |
3718 | } |
3995 | } |
3719 | 3996 | ||
3720 | switch ($type) { |
3997 | switch ($type) { |
3721 | case MessageType::CHANNEL_EXTENDED_DATA: |
3998 | case NET_SSH2_MSG_CHANNEL_EXTENDED_DATA: |
3722 | /* |
3999 | /* |
3723 | if ($client_channel == self::CHANNEL_EXEC) { |
4000 | if ($client_channel == self::CHANNEL_EXEC) { |
3724 | $this->send_channel_packet($client_channel, chr(0)); |
4001 | $this->send_channel_packet($client_channel, chr(0)); |
3725 | } |
4002 | } |
3726 | */ |
4003 | */ |
Line 3728... | Line 4005... | ||
3728 | list($data_type_code, $data) = Strings::unpackSSH2('Ns', $response); |
4005 | list($data_type_code, $data) = Strings::unpackSSH2('Ns', $response); |
3729 | $this->stdErrorLog .= $data; |
4006 | $this->stdErrorLog .= $data; |
3730 | if ($skip_extended || $this->quiet_mode) { |
4007 | if ($skip_extended || $this->quiet_mode) { |
3731 | continue 2; |
4008 | continue 2; |
3732 | } |
4009 | } |
3733 | if ($client_channel == $channel && $this->channel_status[$channel] == MessageType::CHANNEL_DATA) { |
4010 | if ($client_channel == $channel && $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA) { |
3734 | return $data; |
4011 | return $data; |
3735 | } |
4012 | } |
3736 | $this->channel_buffers[$channel][] = chr($type) . $data; |
4013 | $this->channel_buffers[$channel][] = chr($type) . $data; |
3737 | 4014 | ||
3738 | continue 2; |
4015 | continue 2; |
3739 | case MessageType::CHANNEL_REQUEST: |
4016 | case NET_SSH2_MSG_CHANNEL_REQUEST: |
3740 | if ($this->channel_status[$channel] == MessageType::CHANNEL_CLOSE) { |
4017 | if ($this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_CLOSE) { |
3741 | continue 2; |
4018 | continue 2; |
3742 | } |
4019 | } |
3743 | list($value) = Strings::unpackSSH2('s', $response); |
4020 | list($value) = Strings::unpackSSH2('s', $response); |
3744 | switch ($value) { |
4021 | switch ($value) { |
3745 | case 'exit-signal': |
4022 | case 'exit-signal': |
Line 3753... | Line 4030... | ||
3753 | $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): $signal_name"; |
4030 | $this->errors[] = "SSH_MSG_CHANNEL_REQUEST (exit-signal): $signal_name"; |
3754 | if (strlen($error_message)) { |
4031 | if (strlen($error_message)) { |
3755 | $this->errors[count($this->errors) - 1] .= "\r\n$error_message"; |
4032 | $this->errors[count($this->errors) - 1] .= "\r\n$error_message"; |
3756 | } |
4033 | } |
3757 | 4034 | ||
3758 | $this->send_binary_packet(pack('CN', MessageType::CHANNEL_EOF, $this->server_channels[$client_channel])); |
4035 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); |
3759 | $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$channel])); |
4036 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); |
3760 | 4037 | ||
3761 | $this->channel_status[$channel] = MessageType::CHANNEL_EOF; |
4038 | $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_EOF; |
3762 | 4039 | ||
3763 | continue 3; |
4040 | continue 3; |
3764 | case 'exit-status': |
4041 | case 'exit-status': |
3765 | list(, $this->exit_status) = Strings::unpackSSH2('CN', $response); |
4042 | list(, $this->exit_status) = Strings::unpackSSH2('CN', $response); |
3766 | 4043 | ||
Line 3774... | Line 4051... | ||
3774 | continue 3; |
4051 | continue 3; |
3775 | } |
4052 | } |
3776 | } |
4053 | } |
3777 | 4054 | ||
3778 | switch ($this->channel_status[$channel]) { |
4055 | switch ($this->channel_status[$channel]) { |
3779 | case MessageType::CHANNEL_OPEN: |
4056 | case NET_SSH2_MSG_CHANNEL_OPEN: |
3780 | switch ($type) { |
4057 | switch ($type) { |
3781 | case MessageType::CHANNEL_OPEN_CONFIRMATION: |
4058 | case NET_SSH2_MSG_CHANNEL_OPEN_CONFIRMATION: |
3782 | list( |
4059 | list( |
3783 | $this->server_channels[$channel], |
4060 | $this->server_channels[$channel], |
3784 | $window_size, |
4061 | $window_size, |
3785 | $this->packet_size_client_to_server[$channel] |
4062 | $this->packet_size_client_to_server[$channel] |
3786 | ) = Strings::unpackSSH2('NNN', $response); |
4063 | ) = Strings::unpackSSH2('NNN', $response); |
Line 3791... | Line 4068... | ||
3791 | } |
4068 | } |
3792 | $this->window_size_client_to_server[$channel] = $window_size; |
4069 | $this->window_size_client_to_server[$channel] = $window_size; |
3793 | $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended); |
4070 | $result = $client_channel == $channel ? true : $this->get_channel_packet($client_channel, $skip_extended); |
3794 | $this->on_channel_open(); |
4071 | $this->on_channel_open(); |
3795 | return $result; |
4072 | return $result; |
3796 | case MessageType::CHANNEL_OPEN_FAILURE: |
4073 | case NET_SSH2_MSG_CHANNEL_OPEN_FAILURE: |
3797 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
4074 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
3798 | throw new \RuntimeException('Unable to open channel'); |
4075 | throw new \RuntimeException('Unable to open channel'); |
3799 | default: |
4076 | default: |
3800 | if ($client_channel == $channel) { |
4077 | if ($client_channel == $channel) { |
3801 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
4078 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
3802 | throw new \RuntimeException('Unexpected response to open request'); |
4079 | throw new \RuntimeException('Unexpected response to open request'); |
3803 | } |
4080 | } |
3804 | return $this->get_channel_packet($client_channel, $skip_extended); |
4081 | return $this->get_channel_packet($client_channel, $skip_extended); |
3805 | } |
4082 | } |
3806 | break; |
4083 | break; |
3807 | case MessageType::CHANNEL_REQUEST: |
4084 | case NET_SSH2_MSG_CHANNEL_REQUEST: |
3808 | switch ($type) { |
4085 | switch ($type) { |
3809 | case MessageType::CHANNEL_SUCCESS: |
4086 | case NET_SSH2_MSG_CHANNEL_SUCCESS: |
3810 | return true; |
4087 | return true; |
3811 | case MessageType::CHANNEL_FAILURE: |
4088 | case NET_SSH2_MSG_CHANNEL_FAILURE: |
3812 | return false; |
4089 | return false; |
3813 | case MessageType::CHANNEL_DATA: |
4090 | case NET_SSH2_MSG_CHANNEL_DATA: |
3814 | list($data) = Strings::unpackSSH2('s', $response); |
4091 | list($data) = Strings::unpackSSH2('s', $response); |
3815 | $this->channel_buffers[$channel][] = chr($type) . $data; |
4092 | $this->channel_buffers[$channel][] = chr($type) . $data; |
3816 | return $this->get_channel_packet($client_channel, $skip_extended); |
4093 | return $this->get_channel_packet($client_channel, $skip_extended); |
3817 | default: |
4094 | default: |
3818 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
4095 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
3819 | throw new \RuntimeException('Unable to fulfill channel request'); |
4096 | throw new \RuntimeException('Unable to fulfill channel request'); |
3820 | } |
4097 | } |
3821 | case MessageType::CHANNEL_CLOSE: |
4098 | case NET_SSH2_MSG_CHANNEL_CLOSE: |
3822 | return $type == MessageType::CHANNEL_CLOSE ? true : $this->get_channel_packet($client_channel, $skip_extended); |
4099 | return $type == NET_SSH2_MSG_CHANNEL_CLOSE ? true : $this->get_channel_packet($client_channel, $skip_extended); |
3823 | } |
4100 | } |
3824 | } |
4101 | } |
3825 | 4102 | ||
3826 | // ie. $this->channel_status[$channel] == SSHMsg::CHANNEL_DATA |
4103 | // ie. $this->channel_status[$channel] == NET_SSH2_MSG_CHANNEL_DATA |
3827 | 4104 | ||
3828 | switch ($type) { |
4105 | switch ($type) { |
3829 | case MessageType::CHANNEL_DATA: |
4106 | case NET_SSH2_MSG_CHANNEL_DATA: |
3830 | /* |
4107 | /* |
3831 | if ($channel == self::CHANNEL_EXEC) { |
4108 | if ($channel == self::CHANNEL_EXEC) { |
3832 | // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server |
4109 | // SCP requires null packets, such as this, be sent. further, in the case of the ssh.com SSH server |
3833 | // this actually seems to make things twice as fast. more to the point, the message right after |
4110 | // this actually seems to make things twice as fast. more to the point, the message right after |
3834 | // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. |
4111 | // SSH_MSG_CHANNEL_DATA (usually SSH_MSG_IGNORE) won't block for as long as it would have otherwise. |
Line 3849... | Line 4126... | ||
3849 | if ($client_channel == $channel) { |
4126 | if ($client_channel == $channel) { |
3850 | return $data; |
4127 | return $data; |
3851 | } |
4128 | } |
3852 | $this->channel_buffers[$channel][] = chr($type) . $data; |
4129 | $this->channel_buffers[$channel][] = chr($type) . $data; |
3853 | break; |
4130 | break; |
3854 | case MessageType::CHANNEL_CLOSE: |
4131 | case NET_SSH2_MSG_CHANNEL_CLOSE: |
3855 | $this->curTimeout = 5; |
4132 | $this->curTimeout = 5; |
3856 | 4133 | ||
3857 | if ($this->bitmap & self::MASK_SHELL) { |
4134 | if ($this->bitmap & self::MASK_SHELL) { |
3858 | $this->bitmap &= ~self::MASK_SHELL; |
4135 | $this->bitmap &= ~self::MASK_SHELL; |
3859 | } |
4136 | } |
3860 | if ($this->channel_status[$channel] != MessageType::CHANNEL_EOF) { |
4137 | if ($this->channel_status[$channel] != NET_SSH2_MSG_CHANNEL_EOF) { |
3861 | $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$channel])); |
4138 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$channel])); |
3862 | } |
4139 | } |
3863 | 4140 | ||
3864 | $this->channel_status[$channel] = MessageType::CHANNEL_CLOSE; |
4141 | $this->channel_status[$channel] = NET_SSH2_MSG_CHANNEL_CLOSE; |
3865 | if ($client_channel == $channel) { |
4142 | if ($client_channel == $channel) { |
3866 | return true; |
4143 | return true; |
3867 | } |
4144 | } |
3868 | // fall-through |
4145 | // fall-through |
3869 | case MessageType::CHANNEL_EOF: |
4146 | case NET_SSH2_MSG_CHANNEL_EOF: |
3870 | break; |
4147 | break; |
3871 | default: |
4148 | default: |
3872 | $this->disconnect_helper(DisconnectReason::BY_APPLICATION); |
4149 | $this->disconnect_helper(NET_SSH2_DISCONNECT_BY_APPLICATION); |
3873 | throw new \RuntimeException("Error reading channel data ($type)"); |
4150 | throw new \RuntimeException("Error reading channel data ($type)"); |
3874 | } |
4151 | } |
3875 | } |
4152 | } |
3876 | } |
4153 | } |
3877 | 4154 | ||
Line 3882... | Line 4159... | ||
3882 | * |
4159 | * |
3883 | * @param string $data |
4160 | * @param string $data |
3884 | * @param string $logged |
4161 | * @param string $logged |
3885 | * @see self::_get_binary_packet() |
4162 | * @see self::_get_binary_packet() |
3886 | * @return void |
4163 | * @return void |
- | 4164 | * @access private |
|
3887 | */ |
4165 | */ |
3888 | protected function send_binary_packet($data, $logged = null) |
4166 | protected function send_binary_packet($data, $logged = null) |
3889 | { |
4167 | { |
3890 | if (!is_resource($this->fsock) || feof($this->fsock)) { |
4168 | if (!is_resource($this->fsock) || feof($this->fsock)) { |
3891 | $this->bitmap = 0; |
4169 | $this->bitmap = 0; |
Line 4006... | Line 4284... | ||
4006 | $sent = @fputs($this->fsock, $packet); |
4284 | $sent = @fputs($this->fsock, $packet); |
4007 | $stop = microtime(true); |
4285 | $stop = microtime(true); |
4008 | 4286 | ||
4009 | if (defined('NET_SSH2_LOGGING')) { |
4287 | if (defined('NET_SSH2_LOGGING')) { |
4010 | $current = microtime(true); |
4288 | $current = microtime(true); |
4011 | $message_number = sprintf( |
- | |
4012 | '-> %s (since last: %s, network: %ss)', |
- | |
4013 | ($constantName = MessageType::findConstantNameByValue($value = ord($logged[0]), false)) |
4289 | $message_number = isset($this->message_numbers[ord($logged[0])]) ? $this->message_numbers[ord($logged[0])] : 'UNKNOWN (' . ord($logged[0]) . ')'; |
4014 | ? "SSH_MSG_$constantName" |
4290 | $message_number = '-> ' . $message_number . |
4015 | : "UNKNOWN ($value)", |
- | |
4016 | round($current - $this->last_packet, 4), |
4291 | ' (since last: ' . round($current - $this->last_packet, 4) . ', network: ' . round($stop - $start, 4) . 's)'; |
4017 | round($stop - $start, 4) |
- | |
4018 | ); |
- | |
4019 | $this->append_log($message_number, $logged); |
4292 | $this->append_log($message_number, $logged); |
4020 | $this->last_packet = $current; |
4293 | $this->last_packet = $current; |
4021 | } |
4294 | } |
4022 | 4295 | ||
4023 | if (strlen($packet) != $sent) { |
4296 | if (strlen($packet) != $sent) { |
Line 4031... | Line 4304... | ||
4031 | * |
4304 | * |
4032 | * Makes sure that only the last 1MB worth of packets will be logged |
4305 | * Makes sure that only the last 1MB worth of packets will be logged |
4033 | * |
4306 | * |
4034 | * @param string $message_number |
4307 | * @param string $message_number |
4035 | * @param string $message |
4308 | * @param string $message |
- | 4309 | * @access private |
|
4036 | */ |
4310 | */ |
4037 | private function append_log($message_number, $message) |
4311 | private function append_log($message_number, $message) |
4038 | { |
4312 | { |
4039 | if (!defined('NET_SSH2_LOGGING')) { |
- | |
4040 | return; |
- | |
4041 | } |
- | |
4042 | - | ||
4043 | // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) |
4313 | // remove the byte identifying the message type from all but the first two messages (ie. the identification strings) |
4044 | if (strlen($message_number) > 2) { |
4314 | if (strlen($message_number) > 2) { |
4045 | Strings::shift($message); |
4315 | Strings::shift($message); |
4046 | } |
4316 | } |
4047 | 4317 | ||
Line 4135... | Line 4405... | ||
4135 | ); |
4405 | ); |
4136 | 4406 | ||
4137 | $temp = Strings::shift($data, $max_size); |
4407 | $temp = Strings::shift($data, $max_size); |
4138 | $packet = Strings::packSSH2( |
4408 | $packet = Strings::packSSH2( |
4139 | 'CNs', |
4409 | 'CNs', |
4140 | MessageType::CHANNEL_DATA, |
4410 | NET_SSH2_MSG_CHANNEL_DATA, |
4141 | $this->server_channels[$client_channel], |
4411 | $this->server_channels[$client_channel], |
4142 | $temp |
4412 | $temp |
4143 | ); |
4413 | ); |
4144 | $this->window_size_client_to_server[$client_channel] -= strlen($temp); |
4414 | $this->window_size_client_to_server[$client_channel] -= strlen($temp); |
4145 | $this->send_binary_packet($packet); |
4415 | $this->send_binary_packet($packet); |
Line 4154... | Line 4424... | ||
4154 | * for SCP more than anything. |
4424 | * for SCP more than anything. |
4155 | * |
4425 | * |
4156 | * @param int $client_channel |
4426 | * @param int $client_channel |
4157 | * @param bool $want_reply |
4427 | * @param bool $want_reply |
4158 | * @return void |
4428 | * @return void |
- | 4429 | * @access private |
|
4159 | */ |
4430 | */ |
4160 | private function close_channel($client_channel, $want_reply = false) |
4431 | private function close_channel($client_channel, $want_reply = false) |
4161 | { |
4432 | { |
4162 | // see http://tools.ietf.org/html/rfc4254#section-5.3 |
4433 | // see http://tools.ietf.org/html/rfc4254#section-5.3 |
4163 | 4434 | ||
4164 | $this->send_binary_packet(pack('CN', MessageType::CHANNEL_EOF, $this->server_channels[$client_channel])); |
4435 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_EOF, $this->server_channels[$client_channel])); |
4165 | 4436 | ||
4166 | if (!$want_reply) { |
4437 | if (!$want_reply) { |
4167 | $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$client_channel])); |
4438 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); |
4168 | } |
4439 | } |
4169 | 4440 | ||
4170 | $this->channel_status[$client_channel] = MessageType::CHANNEL_CLOSE; |
4441 | $this->channel_status[$client_channel] = NET_SSH2_MSG_CHANNEL_CLOSE; |
4171 | 4442 | ||
4172 | $this->curTimeout = 5; |
4443 | $this->curTimeout = 5; |
4173 | 4444 | ||
4174 | while (!is_bool($this->get_channel_packet($client_channel))) { |
4445 | while (!is_bool($this->get_channel_packet($client_channel))) { |
4175 | } |
4446 | } |
Line 4177... | Line 4448... | ||
4177 | if ($this->is_timeout) { |
4448 | if ($this->is_timeout) { |
4178 | $this->disconnect(); |
4449 | $this->disconnect(); |
4179 | } |
4450 | } |
4180 | 4451 | ||
4181 | if ($want_reply) { |
4452 | if ($want_reply) { |
4182 | $this->send_binary_packet(pack('CN', MessageType::CHANNEL_CLOSE, $this->server_channels[$client_channel])); |
4453 | $this->send_binary_packet(pack('CN', NET_SSH2_MSG_CHANNEL_CLOSE, $this->server_channels[$client_channel])); |
4183 | } |
4454 | } |
4184 | 4455 | ||
4185 | if ($this->bitmap & self::MASK_SHELL) { |
4456 | if ($this->bitmap & self::MASK_SHELL) { |
4186 | $this->bitmap &= ~self::MASK_SHELL; |
4457 | $this->bitmap &= ~self::MASK_SHELL; |
4187 | } |
4458 | } |
Line 4190... | Line 4461... | ||
4190 | /** |
4461 | /** |
4191 | * Disconnect |
4462 | * Disconnect |
4192 | * |
4463 | * |
4193 | * @param int $reason |
4464 | * @param int $reason |
4194 | * @return false |
4465 | * @return false |
- | 4466 | * @access protected |
|
4195 | */ |
4467 | */ |
4196 | protected function disconnect_helper($reason) |
4468 | protected function disconnect_helper($reason) |
4197 | { |
4469 | { |
4198 | if ($this->bitmap & self::MASK_CONNECTED) { |
4470 | if ($this->bitmap & self::MASK_CONNECTED) { |
4199 | $data = Strings::packSSH2('CNss', MessageType::DISCONNECT, $reason, '', ''); |
4471 | $data = Strings::packSSH2('CNss', NET_SSH2_MSG_DISCONNECT, $reason, '', ''); |
4200 | try { |
4472 | try { |
4201 | $this->send_binary_packet($data); |
4473 | $this->send_binary_packet($data); |
4202 | } catch (\Exception $e) { |
4474 | } catch (\Exception $e) { |
4203 | } |
4475 | } |
4204 | } |
4476 | } |
Line 4210... | Line 4482... | ||
4210 | 4482 | ||
4211 | return false; |
4483 | return false; |
4212 | } |
4484 | } |
4213 | 4485 | ||
4214 | /** |
4486 | /** |
- | 4487 | * Define Array |
|
- | 4488 | * |
|
- | 4489 | * Takes any number of arrays whose indices are integers and whose values are strings and defines a bunch of |
|
- | 4490 | * named constants from it, using the value as the name of the constant and the index as the value of the constant. |
|
- | 4491 | * If any of the constants that would be defined already exists, none of the constants will be defined. |
|
- | 4492 | * |
|
- | 4493 | * @param mixed[] ...$args |
|
- | 4494 | * @access protected |
|
- | 4495 | */ |
|
- | 4496 | protected function define_array(...$args) |
|
- | 4497 | { |
|
- | 4498 | foreach ($args as $arg) { |
|
- | 4499 | foreach ($arg as $key => $value) { |
|
- | 4500 | if (!defined($value)) { |
|
- | 4501 | define($value, $key); |
|
- | 4502 | } else { |
|
- | 4503 | break 2; |
|
- | 4504 | } |
|
- | 4505 | } |
|
- | 4506 | } |
|
- | 4507 | } |
|
- | 4508 | ||
- | 4509 | /** |
|
4215 | * Returns a log of the packets that have been sent and received. |
4510 | * Returns a log of the packets that have been sent and received. |
4216 | * |
4511 | * |
4217 | * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') |
4512 | * Returns a string if NET_SSH2_LOGGING == self::LOG_COMPLEX, an array if NET_SSH2_LOGGING == self::LOG_SIMPLE and false if !defined('NET_SSH2_LOGGING') |
4218 | * |
4513 | * |
- | 4514 | * @access public |
|
4219 | * @return array|false|string |
4515 | * @return array|false|string |
4220 | */ |
4516 | */ |
4221 | public function getLog() |
4517 | public function getLog() |
4222 | { |
4518 | { |
4223 | if (!defined('NET_SSH2_LOGGING')) { |
4519 | if (!defined('NET_SSH2_LOGGING')) { |
Line 4238... | Line 4534... | ||
4238 | /** |
4534 | /** |
4239 | * Formats a log for printing |
4535 | * Formats a log for printing |
4240 | * |
4536 | * |
4241 | * @param array $message_log |
4537 | * @param array $message_log |
4242 | * @param array $message_number_log |
4538 | * @param array $message_number_log |
- | 4539 | * @access private |
|
4243 | * @return string |
4540 | * @return string |
4244 | */ |
4541 | */ |
4245 | protected function format_log($message_log, $message_number_log) |
4542 | protected function format_log($message_log, $message_number_log) |
4246 | { |
4543 | { |
4247 | $output = ''; |
4544 | $output = ''; |
Line 4275... | Line 4572... | ||
4275 | * |
4572 | * |
4276 | * Used when channels are created to inform agent |
4573 | * Used when channels are created to inform agent |
4277 | * of said channel opening. Must be called after |
4574 | * of said channel opening. Must be called after |
4278 | * channel open confirmation received |
4575 | * channel open confirmation received |
4279 | * |
4576 | * |
- | 4577 | * @access private |
|
4280 | */ |
4578 | */ |
4281 | private function on_channel_open() |
4579 | private function on_channel_open() |
4282 | { |
4580 | { |
4283 | if (isset($this->agent)) { |
4581 | if (isset($this->agent)) { |
4284 | $this->agent->registerChannelOpen($this); |
4582 | $this->agent->registerChannelOpen($this); |
Line 4290... | Line 4588... | ||
4290 | * the intersection is empty. The order is defined by the first parameter. |
4588 | * the intersection is empty. The order is defined by the first parameter. |
4291 | * |
4589 | * |
4292 | * @param array $array1 |
4590 | * @param array $array1 |
4293 | * @param array $array2 |
4591 | * @param array $array2 |
4294 | * @return mixed False if intersection is empty, else intersected value. |
4592 | * @return mixed False if intersection is empty, else intersected value. |
- | 4593 | * @access private |
|
4295 | */ |
4594 | */ |
4296 | private static function array_intersect_first($array1, $array2) |
4595 | private static function array_intersect_first($array1, $array2) |
4297 | { |
4596 | { |
4298 | foreach ($array1 as $value) { |
4597 | foreach ($array1 as $value) { |
4299 | if (in_array($value, $array2)) { |
4598 | if (in_array($value, $array2)) { |
Line 4305... | Line 4604... | ||
4305 | 4604 | ||
4306 | /** |
4605 | /** |
4307 | * Returns all errors |
4606 | * Returns all errors |
4308 | * |
4607 | * |
4309 | * @return string[] |
4608 | * @return string[] |
- | 4609 | * @access public |
|
4310 | */ |
4610 | */ |
4311 | public function getErrors() |
4611 | public function getErrors() |
4312 | { |
4612 | { |
4313 | return $this->errors; |
4613 | return $this->errors; |
4314 | } |
4614 | } |
4315 | 4615 | ||
4316 | /** |
4616 | /** |
4317 | * Returns the last error |
4617 | * Returns the last error |
4318 | * |
4618 | * |
4319 | * @return string |
4619 | * @return string |
- | 4620 | * @access public |
|
4320 | */ |
4621 | */ |
4321 | public function getLastError() |
4622 | public function getLastError() |
4322 | { |
4623 | { |
4323 | $count = count($this->errors); |
4624 | $count = count($this->errors); |
4324 | 4625 | ||
Line 4329... | Line 4630... | ||
4329 | 4630 | ||
4330 | /** |
4631 | /** |
4331 | * Return the server identification. |
4632 | * Return the server identification. |
4332 | * |
4633 | * |
4333 | * @return string|false |
4634 | * @return string|false |
- | 4635 | * @access public |
|
4334 | */ |
4636 | */ |
4335 | public function getServerIdentification() |
4637 | public function getServerIdentification() |
4336 | { |
4638 | { |
4337 | $this->connect(); |
4639 | $this->connect(); |
4338 | 4640 | ||
Line 4341... | Line 4643... | ||
4341 | 4643 | ||
4342 | /** |
4644 | /** |
4343 | * Returns a list of algorithms the server supports |
4645 | * Returns a list of algorithms the server supports |
4344 | * |
4646 | * |
4345 | * @return array |
4647 | * @return array |
- | 4648 | * @access public |
|
4346 | */ |
4649 | */ |
4347 | public function getServerAlgorithms() |
4650 | public function getServerAlgorithms() |
4348 | { |
4651 | { |
4349 | $this->connect(); |
4652 | $this->connect(); |
4350 | 4653 | ||
Line 4368... | Line 4671... | ||
4368 | 4671 | ||
4369 | /** |
4672 | /** |
4370 | * Returns a list of KEX algorithms that phpseclib supports |
4673 | * Returns a list of KEX algorithms that phpseclib supports |
4371 | * |
4674 | * |
4372 | * @return array |
4675 | * @return array |
- | 4676 | * @access public |
|
4373 | */ |
4677 | */ |
4374 | public static function getSupportedKEXAlgorithms() |
4678 | public static function getSupportedKEXAlgorithms() |
4375 | { |
4679 | { |
4376 | $kex_algorithms = [ |
4680 | $kex_algorithms = [ |
4377 | // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using |
4681 | // Elliptic Curve Diffie-Hellman Key Agreement (ECDH) using |
Line 4404... | Line 4708... | ||
4404 | 4708 | ||
4405 | /** |
4709 | /** |
4406 | * Returns a list of host key algorithms that phpseclib supports |
4710 | * Returns a list of host key algorithms that phpseclib supports |
4407 | * |
4711 | * |
4408 | * @return array |
4712 | * @return array |
- | 4713 | * @access public |
|
4409 | */ |
4714 | */ |
4410 | public static function getSupportedHostKeyAlgorithms() |
4715 | public static function getSupportedHostKeyAlgorithms() |
4411 | { |
4716 | { |
4412 | return [ |
4717 | return [ |
4413 | 'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02 |
4718 | 'ssh-ed25519', // https://tools.ietf.org/html/draft-ietf-curdle-ssh-ed25519-02 |
Line 4423... | Line 4728... | ||
4423 | 4728 | ||
4424 | /** |
4729 | /** |
4425 | * Returns a list of symmetric key algorithms that phpseclib supports |
4730 | * Returns a list of symmetric key algorithms that phpseclib supports |
4426 | * |
4731 | * |
4427 | * @return array |
4732 | * @return array |
- | 4733 | * @access public |
|
4428 | */ |
4734 | */ |
4429 | public static function getSupportedEncryptionAlgorithms() |
4735 | public static function getSupportedEncryptionAlgorithms() |
4430 | { |
4736 | { |
4431 | $algos = [ |
4737 | $algos = [ |
4432 | // from <https://tools.ietf.org/html/rfc5647>: |
4738 | // from <https://tools.ietf.org/html/rfc5647>: |
Line 4442... | Line 4748... | ||
4442 | // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>: |
4748 | // CTR modes from <http://tools.ietf.org/html/rfc4344#section-4>: |
4443 | 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key |
4749 | 'aes128-ctr', // RECOMMENDED AES (Rijndael) in SDCTR mode, with 128-bit key |
4444 | 'aes192-ctr', // RECOMMENDED AES with 192-bit key |
4750 | 'aes192-ctr', // RECOMMENDED AES with 192-bit key |
4445 | 'aes256-ctr', // RECOMMENDED AES with 256-bit key |
4751 | 'aes256-ctr', // RECOMMENDED AES with 256-bit key |
4446 | 4752 | ||
4447 | // from <https://github.com/openssh/openssh-portable/blob/001aa55/PROTOCOL.chacha20poly1305>: |
4753 | // from <https://git.io/fhxOl>: |
4448 | // one of the big benefits of chacha20-poly1305 is speed. the problem is... |
4754 | // one of the big benefits of chacha20-poly1305 is speed. the problem is... |
4449 | // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even |
4755 | // libsodium doesn't generate the poly1305 keys in the way ssh does and openssl's PHP bindings don't even |
4450 | // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20 |
4756 | // seem to support poly1305 currently. so even if libsodium or openssl are being used for the chacha20 |
4451 | // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down. |
4757 | // part, pure-PHP has to be used for the poly1305 part and that's gonna cause a big slow down. |
4452 | // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC |
4758 | // speed-wise it winds up being faster to use AES (when openssl or mcrypt are available) and some HMAC |
Line 4527... | Line 4833... | ||
4527 | 4833 | ||
4528 | /** |
4834 | /** |
4529 | * Returns a list of MAC algorithms that phpseclib supports |
4835 | * Returns a list of MAC algorithms that phpseclib supports |
4530 | * |
4836 | * |
4531 | * @return array |
4837 | * @return array |
- | 4838 | * @access public |
|
4532 | */ |
4839 | */ |
4533 | public static function getSupportedMACAlgorithms() |
4840 | public static function getSupportedMACAlgorithms() |
4534 | { |
4841 | { |
4535 | return [ |
4842 | return [ |
4536 | 'hmac-sha2-256-etm@openssh.com', |
4843 | 'hmac-sha2-256-etm@openssh.com', |
Line 4557... | Line 4864... | ||
4557 | 4864 | ||
4558 | /** |
4865 | /** |
4559 | * Returns a list of compression algorithms that phpseclib supports |
4866 | * Returns a list of compression algorithms that phpseclib supports |
4560 | * |
4867 | * |
4561 | * @return array |
4868 | * @return array |
- | 4869 | * @access public |
|
4562 | */ |
4870 | */ |
4563 | public static function getSupportedCompressionAlgorithms() |
4871 | public static function getSupportedCompressionAlgorithms() |
4564 | { |
4872 | { |
4565 | $algos = ['none']; // REQUIRED no compression |
4873 | $algos = ['none']; // REQUIRED no compression |
4566 | if (function_exists('deflate_init')) { |
4874 | if (function_exists('deflate_init')) { |
Line 4574... | Line 4882... | ||
4574 | * Return list of negotiated algorithms |
4882 | * Return list of negotiated algorithms |
4575 | * |
4883 | * |
4576 | * Uses the same format as https://www.php.net/ssh2-methods-negotiated |
4884 | * Uses the same format as https://www.php.net/ssh2-methods-negotiated |
4577 | * |
4885 | * |
4578 | * @return array |
4886 | * @return array |
- | 4887 | * @access public |
|
4579 | */ |
4888 | */ |
4580 | public function getAlgorithmsNegotiated() |
4889 | public function getAlgorithmsNegotiated() |
4581 | { |
4890 | { |
4582 | $this->connect(); |
4891 | $this->connect(); |
4583 | 4892 | ||
Line 4605... | Line 4914... | ||
4605 | 4914 | ||
4606 | /** |
4915 | /** |
4607 | * Allows you to set the terminal |
4916 | * Allows you to set the terminal |
4608 | * |
4917 | * |
4609 | * @param string $term |
4918 | * @param string $term |
- | 4919 | * @access public |
|
4610 | */ |
4920 | */ |
4611 | public function setTerminal($term) |
4921 | public function setTerminal($term) |
4612 | { |
4922 | { |
4613 | $this->term = $term; |
4923 | $this->term = $term; |
4614 | } |
4924 | } |
Line 4616... | Line 4926... | ||
4616 | /** |
4926 | /** |
4617 | * Accepts an associative array with up to four parameters as described at |
4927 | * Accepts an associative array with up to four parameters as described at |
4618 | * <https://www.php.net/manual/en/function.ssh2-connect.php> |
4928 | * <https://www.php.net/manual/en/function.ssh2-connect.php> |
4619 | * |
4929 | * |
4620 | * @param array $methods |
4930 | * @param array $methods |
- | 4931 | * @access public |
|
4621 | */ |
4932 | */ |
4622 | public function setPreferredAlgorithms(array $methods) |
4933 | public function setPreferredAlgorithms(array $methods) |
4623 | { |
4934 | { |
4624 | $preferred = $methods; |
4935 | $preferred = $methods; |
4625 | 4936 | ||
Line 4702... | Line 5013... | ||
4702 | * |
5013 | * |
4703 | * Quoting from the RFC, "in some jurisdictions, sending a warning message before |
5014 | * Quoting from the RFC, "in some jurisdictions, sending a warning message before |
4704 | * authentication may be relevant for getting legal protection." |
5015 | * authentication may be relevant for getting legal protection." |
4705 | * |
5016 | * |
4706 | * @return string |
5017 | * @return string |
- | 5018 | * @access public |
|
4707 | */ |
5019 | */ |
4708 | public function getBannerMessage() |
5020 | public function getBannerMessage() |
4709 | { |
5021 | { |
4710 | return $this->banner_message; |
5022 | return $this->banner_message; |
4711 | } |
5023 | } |
Line 4717... | Line 5029... | ||
4717 | * is recommended. Returns false if the server signature is not signed correctly with the public host key. |
5029 | * is recommended. Returns false if the server signature is not signed correctly with the public host key. |
4718 | * |
5030 | * |
4719 | * @return string|false |
5031 | * @return string|false |
4720 | * @throws \RuntimeException on badly formatted keys |
5032 | * @throws \RuntimeException on badly formatted keys |
4721 | * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format |
5033 | * @throws \phpseclib3\Exception\NoSupportedAlgorithmsException when the key isn't in a supported format |
- | 5034 | * @access public |
|
4722 | */ |
5035 | */ |
4723 | public function getServerPublicHostKey() |
5036 | public function getServerPublicHostKey() |
4724 | { |
5037 | { |
4725 | if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { |
5038 | if (!($this->bitmap & self::MASK_CONSTRUCTOR)) { |
4726 | $this->connect(); |
5039 | $this->connect(); |
Line 4786... | Line 5099... | ||
4786 | $hash = 'sha1'; |
5099 | $hash = 'sha1'; |
4787 | } |
5100 | } |
4788 | $key = $key->withHash($hash); |
5101 | $key = $key->withHash($hash); |
4789 | break; |
5102 | break; |
4790 | default: |
5103 | default: |
4791 | $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE); |
5104 | $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); |
4792 | throw new NoSupportedAlgorithmsException('Unsupported signature format'); |
5105 | throw new NoSupportedAlgorithmsException('Unsupported signature format'); |
4793 | } |
5106 | } |
4794 | 5107 | ||
4795 | if (!$key->verify($this->exchange_hash, $signature)) { |
5108 | if (!$key->verify($this->exchange_hash, $signature)) { |
4796 | return $this->disconnect_helper(DisconnectReason::HOST_KEY_NOT_VERIFIABLE); |
5109 | return $this->disconnect_helper(NET_SSH2_DISCONNECT_HOST_KEY_NOT_VERIFIABLE); |
4797 | }; |
5110 | }; |
4798 | 5111 | ||
4799 | return $this->signature_format . ' ' . $server_public_host_key; |
5112 | return $this->signature_format . ' ' . $server_public_host_key; |
4800 | } |
5113 | } |
4801 | 5114 | ||
4802 | /** |
5115 | /** |
4803 | * Returns the exit status of an SSH command or false. |
5116 | * Returns the exit status of an SSH command or false. |
4804 | * |
5117 | * |
4805 | * @return false|int |
5118 | * @return false|int |
- | 5119 | * @access public |
|
4806 | */ |
5120 | */ |
4807 | public function getExitStatus() |
5121 | public function getExitStatus() |
4808 | { |
5122 | { |
4809 | if (is_null($this->exit_status)) { |
5123 | if (is_null($this->exit_status)) { |
4810 | return false; |
5124 | return false; |
Line 4814... | Line 5128... | ||
4814 | 5128 | ||
4815 | /** |
5129 | /** |
4816 | * Returns the number of columns for the terminal window size. |
5130 | * Returns the number of columns for the terminal window size. |
4817 | * |
5131 | * |
4818 | * @return int |
5132 | * @return int |
- | 5133 | * @access public |
|
4819 | */ |
5134 | */ |
4820 | public function getWindowColumns() |
5135 | public function getWindowColumns() |
4821 | { |
5136 | { |
4822 | return $this->windowColumns; |
5137 | return $this->windowColumns; |
4823 | } |
5138 | } |
4824 | 5139 | ||
4825 | /** |
5140 | /** |
4826 | * Returns the number of rows for the terminal window size. |
5141 | * Returns the number of rows for the terminal window size. |
4827 | * |
5142 | * |
4828 | * @return int |
5143 | * @return int |
- | 5144 | * @access public |
|
4829 | */ |
5145 | */ |
4830 | public function getWindowRows() |
5146 | public function getWindowRows() |
4831 | { |
5147 | { |
4832 | return $this->windowRows; |
5148 | return $this->windowRows; |
4833 | } |
5149 | } |
4834 | 5150 | ||
4835 | /** |
5151 | /** |
4836 | * Sets the number of columns for the terminal window size. |
5152 | * Sets the number of columns for the terminal window size. |
4837 | * |
5153 | * |
4838 | * @param int $value |
5154 | * @param int $value |
- | 5155 | * @access public |
|
4839 | */ |
5156 | */ |
4840 | public function setWindowColumns($value) |
5157 | public function setWindowColumns($value) |
4841 | { |
5158 | { |
4842 | $this->windowColumns = $value; |
5159 | $this->windowColumns = $value; |
4843 | } |
5160 | } |
4844 | 5161 | ||
4845 | /** |
5162 | /** |
4846 | * Sets the number of rows for the terminal window size. |
5163 | * Sets the number of rows for the terminal window size. |
4847 | * |
5164 | * |
4848 | * @param int $value |
5165 | * @param int $value |
- | 5166 | * @access public |
|
4849 | */ |
5167 | */ |
4850 | public function setWindowRows($value) |
5168 | public function setWindowRows($value) |
4851 | { |
5169 | { |
4852 | $this->windowRows = $value; |
5170 | $this->windowRows = $value; |
4853 | } |
5171 | } |
Line 4855... | Line 5173... | ||
4855 | /** |
5173 | /** |
4856 | * Sets the number of columns and rows for the terminal window size. |
5174 | * Sets the number of columns and rows for the terminal window size. |
4857 | * |
5175 | * |
4858 | * @param int $columns |
5176 | * @param int $columns |
4859 | * @param int $rows |
5177 | * @param int $rows |
- | 5178 | * @access public |
|
4860 | */ |
5179 | */ |
4861 | public function setWindowSize($columns = 80, $rows = 24) |
5180 | public function setWindowSize($columns = 80, $rows = 24) |
4862 | { |
5181 | { |
4863 | $this->windowColumns = $columns; |
5182 | $this->windowColumns = $columns; |
4864 | $this->windowRows = $rows; |
5183 | $this->windowRows = $rows; |
Line 4866... | Line 5185... | ||
4866 | 5185 | ||
4867 | /** |
5186 | /** |
4868 | * To String Magic Method |
5187 | * To String Magic Method |
4869 | * |
5188 | * |
4870 | * @return string |
5189 | * @return string |
- | 5190 | * @access public |
|
4871 | */ |
5191 | */ |
4872 | #[\ReturnTypeWillChange] |
5192 | #[\ReturnTypeWillChange] |
4873 | public function __toString() |
5193 | public function __toString() |
4874 | { |
5194 | { |
4875 | return $this->getResourceId(); |
5195 | return $this->getResourceId(); |
Line 4921... | Line 5241... | ||
4921 | $temp[$key] = $ref->get(); |
5241 | $temp[$key] = $ref->get(); |
4922 | } |
5242 | } |
4923 | return $temp; |
5243 | return $temp; |
4924 | } |
5244 | } |
4925 | 5245 | ||
4926 | /** |
5246 | /* |
4927 | * Update packet types in log history |
5247 | * Update packet types in log history |
4928 | * |
5248 | * |
4929 | * @param string $old |
5249 | * @param string $old |
4930 | * @param string $new |
5250 | * @param string $new |
- | 5251 | * @access private |
|
4931 | */ |
5252 | */ |
4932 | private function updateLogHistory($old, $new) |
5253 | private function updateLogHistory($old, $new) |
4933 | { |
5254 | { |
4934 | if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { |
5255 | if (defined('NET_SSH2_LOGGING') && NET_SSH2_LOGGING == self::LOG_COMPLEX) { |
4935 | $this->message_number_log[count($this->message_number_log) - 1] = str_replace( |
5256 | $this->message_number_log[count($this->message_number_log) - 1] = str_replace( |