1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Sara Golemon <pollita@php.net>                               |
16    +----------------------------------------------------------------------+
17 */
18 
19 #ifdef HAVE_CONFIG_H
20 #include "config.h"
21 #endif
22 
23 #include "php.h"
24 #include "ext/standard/info.h"
25 #include "ext/standard/file.h"
26 #include "php_ssh2.h"
27 #include "main/php_network.h"
28 
29 #if (OPENSSL_VERSION_NUMBER >= 0x00908000L)
30 #include <openssl/applink.c>
31 #endif
32 
33 /* Internal Constants */
34 #ifndef SHA_DIGEST_LENGTH
35 #define SHA_DIGEST_LENGTH	20
36 #endif
37 
38 #ifndef MD5_DIGEST_LENGTH
39 #define MD5_DIGEST_LENGTH	16
40 #endif
41 
42 /* True global resources - no need for thread safety here */
43 int le_ssh2_session;
44 int le_ssh2_listener;
45 int le_ssh2_sftp;
46 int le_ssh2_pkey_subsys;
47 
48 /* *************
49    * Callbacks *
50    ************* */
51 
52 /* {{{ php_ssh2_alloc_cb
53  * Wrap emalloc()
54  */
LIBSSH2_ALLOC_FUNC(php_ssh2_alloc_cb)55 static LIBSSH2_ALLOC_FUNC(php_ssh2_alloc_cb)
56 {
57 	return emalloc(count);
58 }
59 /* }}} */
60 
61 /* {{{ php_ssh2_free_cb
62  * Wrap efree()
63  */
LIBSSH2_FREE_FUNC(php_ssh2_free_cb)64 static LIBSSH2_FREE_FUNC(php_ssh2_free_cb)
65 {
66 	efree(ptr);
67 }
68 /* }}} */
69 
70 /* {{{ php_ssh2_realloc_cb
71  * Wrap erealloc()
72  */
LIBSSH2_REALLOC_FUNC(php_ssh2_realloc_cb)73 static LIBSSH2_REALLOC_FUNC(php_ssh2_realloc_cb)
74 {
75 	return erealloc(ptr, count);
76 }
77 /* }}} */
78 
79 /* {{{ php_ssh2_debug_cb
80  * Debug packets
81  */
LIBSSH2_DEBUG_FUNC(php_ssh2_debug_cb)82 LIBSSH2_DEBUG_FUNC(php_ssh2_debug_cb)
83 {
84 	php_ssh2_session_data *data;
85 	zval args[3];
86 
87 	if (!abstract || !*abstract) {
88 		return;
89 	}
90 	data = (php_ssh2_session_data*)*abstract;
91 	if (!data->debug_cb) {
92 		return;
93 	}
94 
95 	ZVAL_STRINGL(&args[0], message, message_len);
96 	ZVAL_STRINGL(&args[1], language, language_len);
97 	ZVAL_LONG(&args[2], always_display);
98 
99 	zval retval;
100 	if (FAILURE == call_user_function(NULL, NULL, data->debug_cb, &retval, 3, args)) {
101 		php_error_docref(NULL, E_WARNING, "Failure calling debug callback");
102 	}
103 
104 	if (!Z_ISUNDEF(retval)) {
105 		zval_ptr_dtor(&retval);
106 	}
107 }
108 /* }}} */
109 
110 /* {{{ php_ssh2_ignore_cb
111  * Ignore packets
112  */
LIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb)113 LIBSSH2_IGNORE_FUNC(php_ssh2_ignore_cb)
114 {
115 	php_ssh2_session_data *data;
116 	zval zretval;
117 	zval args[1];
118 
119 	if (!abstract || !*abstract) {
120 		return;
121 	}
122 	data = (php_ssh2_session_data*)*abstract;
123 	if (!data->ignore_cb) {
124 		return;
125 	}
126 
127 	ZVAL_STRINGL(&args[0], message, message_len);
128 
129 	if (FAILURE == call_user_function(NULL, NULL, data->ignore_cb, &zretval, 1, args)) {
130 		php_error_docref(NULL, E_WARNING, "Failure calling ignore callback");
131 	}
132 	if (Z_TYPE_P(&zretval) != IS_UNDEF) {
133 		zval_ptr_dtor(&zretval);
134 	}
135 }
136 /* }}} */
137 
138 /* {{{ php_ssh2_macerror_cb
139  * Called when a MAC error occurs, offers the chance to ignore
140  * WHY ARE YOU IGNORING MAC ERRORS??????
141  */
LIBSSH2_MACERROR_FUNC(php_ssh2_macerror_cb)142 LIBSSH2_MACERROR_FUNC(php_ssh2_macerror_cb)
143 {
144 	php_ssh2_session_data *data;
145 	zval zretval;
146 	zval args[1];
147 	int retval = -1;
148 
149 	if (!abstract || !*abstract) {
150 		return -1;
151 	}
152 	data = (php_ssh2_session_data*)*abstract;
153 	if (!data->macerror_cb) {
154 		return -1;
155 	}
156 
157 	ZVAL_STRINGL(&args[0], packet, packet_len);
158 
159 	if (FAILURE == call_user_function(NULL, NULL, data->macerror_cb, &zretval, 1, args)) {
160 		php_error_docref(NULL, E_WARNING, "Failure calling macerror callback");
161 	} else {
162 		retval = zval_is_true(&zretval) ? 0 : -1;
163 	}
164 	if (Z_TYPE_P(&zretval) != IS_UNDEF) {
165 		zval_ptr_dtor(&zretval);
166 	}
167 
168 	return retval;
169 }
170 /* }}} */
171 
172 /* {{{ php_ssh2_disconnect_cb
173  * Connection closed by foreign host
174  */
LIBSSH2_DISCONNECT_FUNC(php_ssh2_disconnect_cb)175 LIBSSH2_DISCONNECT_FUNC(php_ssh2_disconnect_cb)
176 {
177 	php_ssh2_session_data *data;
178 	zval args[3];
179 
180 	if (!abstract || !*abstract) {
181 		return;
182 	}
183 	data = (php_ssh2_session_data*)*abstract;
184 	if (!data->disconnect_cb) {
185 		return;
186 	}
187 
188 	ZVAL_LONG(&args[0], reason);
189 	ZVAL_STRINGL(&args[1], message, message_len);
190 	ZVAL_STRINGL(&args[2], language, language_len);
191 
192 	zval retval;
193 	if (FAILURE == call_user_function(NULL, NULL, data->disconnect_cb, &retval, 3, args)) {
194 		php_error_docref(NULL, E_WARNING, "Failure calling disconnect callback");
195 	}
196 
197 	if (!Z_ISUNDEF(retval)) {
198 		zval_ptr_dtor(&retval);
199 	}
200 }
201 /* }}} */
202 
203 
204 
205 /* *****************
206    * Userspace API *
207    ***************** */
208 
209 /* {{{ php_ssh2_set_callback
210  * Try to set a method if it's passed in with the hash table
211  */
php_ssh2_set_callback(LIBSSH2_SESSION * session,HashTable * ht,char * callback,int callback_len,int callback_type,php_ssh2_session_data * data)212 static int php_ssh2_set_callback(LIBSSH2_SESSION *session, HashTable *ht, char *callback, int callback_len, int callback_type, php_ssh2_session_data *data)
213 {
214 	zval *handler, *copyval;
215 	void *internal_handler;
216 	zend_string *callback_zstring;
217 
218 	callback_zstring = zend_string_init(callback, callback_len, 0);
219 	if ((handler = zend_hash_find(ht, callback_zstring)) == NULL) {
220 			zend_string_release(callback_zstring);
221 		return 0;
222 	}
223 	zend_string_release(callback_zstring);
224 
225 	if (!zend_is_callable(handler, 0, NULL)) {
226 		return -1;
227 	}
228 
229 	copyval = emalloc(sizeof(zval));
230 	ZVAL_COPY(copyval, handler);
231 
232 	switch (callback_type) {
233 		case LIBSSH2_CALLBACK_IGNORE:
234 			internal_handler = php_ssh2_ignore_cb;
235 			if (data->ignore_cb) {
236 				zval_ptr_dtor(data->ignore_cb);
237 			}
238 			data->ignore_cb = copyval;
239 			break;
240 		case LIBSSH2_CALLBACK_DEBUG:
241 			internal_handler = php_ssh2_debug_cb;
242 			if (data->debug_cb) {
243 				zval_ptr_dtor(data->debug_cb);
244 			}
245 			data->debug_cb = copyval;
246 			break;
247 		case LIBSSH2_CALLBACK_MACERROR:
248 			internal_handler = php_ssh2_macerror_cb;
249 			if (data->macerror_cb) {
250 				zval_ptr_dtor(data->macerror_cb);
251 			}
252 			data->macerror_cb = copyval;
253 			break;
254 		case LIBSSH2_CALLBACK_DISCONNECT:
255 			internal_handler = php_ssh2_disconnect_cb;
256 			if (data->disconnect_cb) {
257 				zval_ptr_dtor(data->disconnect_cb);
258 			}
259 			data->disconnect_cb = copyval;
260 			break;
261 		default:
262 			zval_ptr_dtor(copyval);
263 			return -1;
264 	}
265 
266 	libssh2_session_callback_set(session, callback_type, internal_handler);
267 
268 	return 0;
269 }
270 /* }}} */
271 
272 /* {{{ php_ssh2_set_method
273  * Try to set a method if it's passed in with the hash table
274  */
php_ssh2_set_method(LIBSSH2_SESSION * session,HashTable * ht,char * method,int method_len,int method_type)275 static int php_ssh2_set_method(LIBSSH2_SESSION *session, HashTable *ht, char *method, int method_len, int method_type)
276 {
277 	zval *value;
278 	zend_string *method_zstring;
279 
280 
281 	method_zstring = zend_string_init(method, method_len, 0);
282 	if ((value = zend_hash_find(ht, method_zstring)) == NULL) {
283 		zend_string_release(method_zstring);
284 		return 0;
285 	}
286 	zend_string_release(method_zstring);
287 
288 	if ((Z_TYPE_P(value) != IS_STRING)) {
289 		return -1;
290 	}
291 
292 	return libssh2_session_method_pref(session, method_type, Z_STRVAL_P(value));
293 }
294 /* }}} */
295 
296 /* {{{ php_ssh2_session_connect
297  * Connect to an SSH server with requested methods
298  */
php_ssh2_session_connect(char * host,int port,zval * methods,zval * callbacks)299 LIBSSH2_SESSION *php_ssh2_session_connect(char *host, int port, zval *methods, zval *callbacks)
300 {
301 	LIBSSH2_SESSION *session;
302 	int socket;
303 	php_ssh2_session_data *data;
304 	struct timeval tv;
305 	zend_string *hash_lookup_zstring;
306 
307 	tv.tv_sec = FG(default_socket_timeout);
308 	tv.tv_usec = 0;
309 
310 	socket = php_network_connect_socket_to_host(host, port, SOCK_STREAM, 0, &tv, NULL, NULL, NULL, 0, STREAM_SOCKOP_NONE);
311 
312 	if (socket <= 0) {
313 		php_error_docref(NULL, E_WARNING, "Unable to connect to %s on port %d", host, port);
314 		return NULL;
315 	}
316 
317 	data = ecalloc(1, sizeof(php_ssh2_session_data));
318 	data->socket = socket;
319 
320 	session = libssh2_session_init_ex(php_ssh2_alloc_cb, php_ssh2_free_cb, php_ssh2_realloc_cb, data);
321 	if (!session) {
322 		php_error_docref(NULL, E_WARNING, "Unable to initialize SSH2 session");
323 		efree(data);
324 		closesocket(socket);
325 		return NULL;
326 	}
327 	libssh2_banner_set(session, LIBSSH2_SSH_DEFAULT_BANNER " PHP");
328 
329 	/* Override method preferences */
330 	if (methods) {
331 		zval *container;
332 
333 		if (php_ssh2_set_method(session, HASH_OF(methods), "kex", sizeof("kex") - 1, LIBSSH2_METHOD_KEX)) {
334 			php_error_docref(NULL, E_WARNING, "Failed overriding KEX method");
335 		}
336 		if (php_ssh2_set_method(session, HASH_OF(methods), "hostkey", sizeof("hostkey") - 1, LIBSSH2_METHOD_HOSTKEY)) {
337 			php_error_docref(NULL, E_WARNING, "Failed overriding HOSTKEY method");
338 		}
339 
340 		hash_lookup_zstring = zend_string_init("client_to_server", sizeof("client_to_server") - 1, 0);
341 		if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL && Z_TYPE_P(container) == IS_ARRAY) {
342 			if (php_ssh2_set_method(session, HASH_OF(container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_CS)) {
343 				php_error_docref(NULL, E_WARNING, "Failed overriding client to server CRYPT method");
344 			}
345 			if (php_ssh2_set_method(session, HASH_OF(container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_CS)) {
346 				php_error_docref(NULL, E_WARNING, "Failed overriding client to server MAC method");
347 			}
348 			if (php_ssh2_set_method(session, HASH_OF(container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_CS)) {
349 				php_error_docref(NULL, E_WARNING, "Failed overriding client to server COMP method");
350 			}
351 			if (php_ssh2_set_method(session, HASH_OF(container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_CS)) {
352 				php_error_docref(NULL, E_WARNING, "Failed overriding client to server LANG method");
353 			}
354 		}
355 		zend_string_release(hash_lookup_zstring);
356 
357 		hash_lookup_zstring = zend_string_init("server_to_client", sizeof("server_to_client") - 1, 0);
358 		if ((container = zend_hash_find(HASH_OF(methods), hash_lookup_zstring)) != NULL && Z_TYPE_P(container) == IS_ARRAY) {
359 			if (php_ssh2_set_method(session, HASH_OF(container), "crypt", sizeof("crypt") - 1, LIBSSH2_METHOD_CRYPT_SC)) {
360 				php_error_docref(NULL, E_WARNING, "Failed overriding server to client CRYPT method");
361 			}
362 			if (php_ssh2_set_method(session, HASH_OF(container), "mac", sizeof("mac") - 1, LIBSSH2_METHOD_MAC_SC)) {
363 				php_error_docref(NULL, E_WARNING, "Failed overriding server to client MAC method");
364 			}
365 			if (php_ssh2_set_method(session, HASH_OF(container), "comp", sizeof("comp") - 1, LIBSSH2_METHOD_COMP_SC)) {
366 				php_error_docref(NULL, E_WARNING, "Failed overriding server to client COMP method");
367 			}
368 			if (php_ssh2_set_method(session, HASH_OF(container), "lang", sizeof("lang") - 1, LIBSSH2_METHOD_LANG_SC)) {
369 				php_error_docref(NULL, E_WARNING, "Failed overriding server to client LANG method");
370 			}
371 		}
372 		zend_string_release(hash_lookup_zstring);
373 
374 	}
375 
376 	/* Register Callbacks */
377 	if (callbacks) {
378 		/* ignore debug disconnect macerror */
379 
380 		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "ignore", sizeof("ignore") - 1, LIBSSH2_CALLBACK_IGNORE, data)) {
381 			php_error_docref(NULL, E_WARNING, "Failed setting IGNORE callback");
382 		}
383 
384 		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "debug", sizeof("debug") - 1, LIBSSH2_CALLBACK_DEBUG, data)) {
385 			php_error_docref(NULL, E_WARNING, "Failed setting DEBUG callback");
386 		}
387 
388 		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "macerror", sizeof("macerror") - 1, LIBSSH2_CALLBACK_MACERROR, data)) {
389 			php_error_docref(NULL, E_WARNING, "Failed setting MACERROR callback");
390 		}
391 
392 		if (php_ssh2_set_callback(session, HASH_OF(callbacks), "disconnect", sizeof("disconnect") - 1, LIBSSH2_CALLBACK_DISCONNECT, data)) {
393 			php_error_docref(NULL, E_WARNING, "Failed setting DISCONNECT callback");
394 		}
395 	}
396 
397 	if (libssh2_session_startup(session, socket)) {
398 		int last_error = 0;
399 		char *error_msg = NULL;
400 
401 		last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);
402 		php_error_docref(NULL, E_WARNING, "Error starting up SSH connection(%d): %s", last_error, error_msg);
403 		closesocket(socket);
404 		libssh2_session_free(session);
405 		efree(data);
406 		return NULL;
407 	}
408 
409 	return session;
410 }
411 /* }}} */
412 
413 /* {{{ proto resource ssh2_connect(string host[, int port[, array methods[, array callbacks]]])
414  * Establish a connection to a remote SSH server and return a resource on success, false on error
415  */
PHP_FUNCTION(ssh2_connect)416 PHP_FUNCTION(ssh2_connect)
417 {
418 	LIBSSH2_SESSION *session;
419 	zval *methods = NULL, *callbacks = NULL;
420 	char *host;
421 	zend_long port = PHP_SSH2_DEFAULT_PORT;
422 	size_t host_len;
423 
424 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "s|la!a!", &host, &host_len, &port, &methods, &callbacks) == FAILURE) {
425 		return;
426 	}
427 
428 	session = php_ssh2_session_connect(host, port, methods, callbacks);
429 	if (!session) {
430 		php_error_docref(NULL, E_WARNING, "Unable to connect to %s", host);
431 		RETURN_FALSE;
432 	}
433 
434 	RETURN_RES(zend_register_resource(session, le_ssh2_session));
435 }
436 /* }}} */
437 
438 /* {{{ proto resource ssh2_disconnect(resource session)
439  * close a connection to a remote SSH server and return a true on success, false on error.
440  */
PHP_FUNCTION(ssh2_disconnect)441 PHP_FUNCTION(ssh2_disconnect)
442 {
443 	zval *zsession;
444 
445 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) {
446 		RETURN_FALSE;
447 	}
448 
449 	zend_list_close(Z_RES_P(zsession));
450 
451 	RETURN_TRUE;
452 }
453 /* }}} */
454 
455 /* {{{ proto array ssh2_methods_negotiated(resource session)
456  * Return list of negotiaed methods
457  */
PHP_FUNCTION(ssh2_methods_negotiated)458 PHP_FUNCTION(ssh2_methods_negotiated)
459 {
460 	LIBSSH2_SESSION *session;
461 	zval *zsession, endpoint;
462 	char *kex, *hostkey, *crypt_cs, *crypt_sc, *mac_cs, *mac_sc, *comp_cs, *comp_sc, *lang_cs, *lang_sc;
463 
464 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) {
465 		return;
466 	}
467 
468 	if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
469 		RETURN_FALSE;
470 	}
471 
472 	kex = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_KEX);
473 	hostkey = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_HOSTKEY);
474 	crypt_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_CS);
475 	crypt_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_CRYPT_SC);
476 	mac_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_CS);
477 	mac_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_MAC_SC);
478 	comp_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_CS);
479 	comp_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_COMP_SC);
480 	lang_cs = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_CS);
481 	lang_sc = (char*)libssh2_session_methods(session, LIBSSH2_METHOD_LANG_SC);
482 
483 	array_init(return_value);
484 	add_assoc_string(return_value, "kex", kex);
485 	add_assoc_string(return_value, "hostkey", hostkey);
486 
487 	array_init(&endpoint);
488 	add_assoc_string(&endpoint, "crypt", crypt_cs);
489 	add_assoc_string(&endpoint, "mac", mac_cs);
490 	add_assoc_string(&endpoint, "comp", comp_cs);
491 	add_assoc_string(&endpoint, "lang", lang_cs);
492 	add_assoc_zval(return_value, "client_to_server", &endpoint);
493 
494 	array_init(&endpoint);
495 	add_assoc_string(&endpoint, "crypt", crypt_sc);
496 	add_assoc_string(&endpoint, "mac", mac_sc);
497 	add_assoc_string(&endpoint, "comp", comp_sc);
498 	add_assoc_string(&endpoint, "lang", lang_sc);
499 	add_assoc_zval(return_value, "server_to_client", &endpoint);
500 }
501 /* }}} */
502 
503 /* {{{ proto string ssh2_fingerprint(resource session[, int flags])
504  * Returns a server hostkey hash from an active session
505  * Defaults to MD5 fingerprint encoded as ASCII hex values
506  */
PHP_FUNCTION(ssh2_fingerprint)507 PHP_FUNCTION(ssh2_fingerprint)
508 {
509 	LIBSSH2_SESSION *session;
510 	zval *zsession;
511 	const char *fingerprint;
512 	zend_long flags = 0;
513 	int i, fingerprint_len;
514 
515 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zsession, &flags) == FAILURE) {
516 		return;
517 	}
518 	fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH;
519 
520 	if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
521 		RETURN_FALSE;
522 	}
523 
524 	fingerprint = (char*)libssh2_hostkey_hash(session, (flags & PHP_SSH2_FINGERPRINT_SHA1) ? LIBSSH2_HOSTKEY_HASH_SHA1 : LIBSSH2_HOSTKEY_HASH_MD5);
525 	if (!fingerprint) {
526 		php_error_docref(NULL, E_WARNING, "Unable to retrieve fingerprint from specified session");
527 		RETURN_FALSE;
528 	}
529 
530 	for(i = 0; i < fingerprint_len; i++) {
531 		if (fingerprint[i] != '\0') {
532 			goto fingerprint_good;
533 		}
534 	}
535 	php_error_docref(NULL, E_WARNING, "No fingerprint available using specified hash");
536 	RETURN_NULL();
537  fingerprint_good:
538 	if (flags & PHP_SSH2_FINGERPRINT_RAW) {
539 		RETURN_STRINGL(fingerprint, fingerprint_len);
540 	} else {
541 		char *hexchars;
542 
543 		hexchars = emalloc((fingerprint_len * 2) + 1);
544 		for(i = 0; i < fingerprint_len; i++) {
545 			snprintf(hexchars + (2 * i), 3, "%02X", (unsigned char)fingerprint[i]);
546 		}
547 		ZVAL_STRINGL(return_value, hexchars, 2 * fingerprint_len);
548 		efree(hexchars);
549 	}
550 }
551 /* }}} */
552 
553 /* {{{ proto array ssh2_auth_none(resource session, string username)
554  * Attempt "none" authentication, returns a list of allowed methods on failed authentication,
555  * false on utter failure, or true on success
556  */
PHP_FUNCTION(ssh2_auth_none)557 PHP_FUNCTION(ssh2_auth_none)
558 {
559 	LIBSSH2_SESSION *session;
560 	zval *zsession;
561 	char *username, *methods, *s, *p;
562 	size_t username_len;
563 
564 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &zsession, &username, &username_len) == FAILURE) {
565 		return;
566 	}
567 
568 	if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) {
569 		RETURN_FALSE;
570 	}
571 
572 	s = methods = libssh2_userauth_list(session, username, username_len);
573 	if (!methods) {
574 		/* Either bad failure, or unexpected success */
575 		RETURN_BOOL(libssh2_userauth_authenticated(session));
576 	}
577 
578 	array_init(return_value);
579 	while ((p = strchr(s, ','))) {
580 		if ((p - s) > 0) {
581 			add_next_index_stringl(return_value, s, p - s);
582 		}
583 		s = p + 1;
584 	}
585 	if (strlen(s)) {
586 		add_next_index_string(return_value, s);
587 	}
588 }
589 /* }}} */
590 
591 char *password_for_kbd_callback;
592 
kbd_callback(const char * name,int name_len,const char * instruction,int instruction_len,int num_prompts,const LIBSSH2_USERAUTH_KBDINT_PROMPT * prompts,LIBSSH2_USERAUTH_KBDINT_RESPONSE * responses,void ** abstract)593 static void kbd_callback(const char *name, int name_len,
594 							const char *instruction, int instruction_len,
595 							int num_prompts,
596 							const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
597 							LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
598 							void **abstract)
599 {
600 	(void)name;
601 	(void)name_len;
602 	(void)instruction;
603 	(void)instruction_len;
604 	if (num_prompts == 1) {
605 		responses[0].text = estrdup(password_for_kbd_callback);
606 		responses[0].length = strlen(password_for_kbd_callback);
607 	}
608 	(void)prompts;
609 	(void)abstract;
610 }
611 
612 /* {{{ proto bool ssh2_auth_password(resource session, string username, string password)
613  * Authenticate over SSH using a plain password
614  */
PHP_FUNCTION(ssh2_auth_password)615 PHP_FUNCTION(ssh2_auth_password)
616 {
617 	LIBSSH2_SESSION *session;
618 	zval *zsession;
619 	zend_string *username, *password;
620 	char *userauthlist;
621 
622 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSS", &zsession, &username, &password) == FAILURE) {
623 		return;
624 	}
625 
626 	SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
627 
628 	userauthlist = libssh2_userauth_list(session, username->val, username->len);
629 
630 	if (userauthlist != NULL) {
631 		password_for_kbd_callback = password->val;
632 		if (strstr(userauthlist, "keyboard-interactive") != NULL) {
633 			if (libssh2_userauth_keyboard_interactive(session, username->val, &kbd_callback) == 0) {
634 				RETURN_TRUE;
635 			}
636 		}
637 
638 		/* TODO: Support password change callback */
639 		if (libssh2_userauth_password_ex(session, username->val, username->len, password->val, password->len, NULL)) {
640 			php_error_docref(NULL, E_WARNING, "Authentication failed for %s using password", username->val);
641 			RETURN_FALSE;
642 		}
643 	}
644 
645 	RETURN_TRUE;
646 }
647 /* }}} */
648 
649 /* {{{ proto bool ssh2_auth_pubkey_file(resource session, string username, string pubkeyfile, string privkeyfile[, string passphrase])
650  * Authenticate using a public key
651  */
PHP_FUNCTION(ssh2_auth_pubkey_file)652 PHP_FUNCTION(ssh2_auth_pubkey_file)
653 {
654 	LIBSSH2_SESSION *session;
655 	zval *zsession;
656 	zend_string *username, *pubkey, *privkey, *passphrase;
657 #ifndef PHP_WIN32
658 	zend_string *newpath;
659 	struct passwd *pws;
660 #endif
661 
662 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rSSS|S", &zsession,	&username, &pubkey, &privkey, &passphrase) == FAILURE) {
663 		return;
664 	}
665 
666 	if (php_check_open_basedir(ZSTR_VAL(pubkey)) || php_check_open_basedir(ZSTR_VAL(privkey))) {
667 		RETURN_FALSE;
668 	}
669 
670 	SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
671 #ifndef PHP_WIN32
672 	/* Explode '~/paths' stopgap fix because libssh2 does not accept tilde for homedir
673 	  This should be ifdef'ed when a fix is available to support older libssh2 versions*/
674 	pws = getpwuid(geteuid());
675 	if (ZSTR_LEN(pubkey) >= 2 && *(ZSTR_VAL(pubkey)) == '~' && *(ZSTR_VAL(pubkey)+1) == '/') {
676 		newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(pubkey), 0);
677 		strcpy(ZSTR_VAL(newpath), pws->pw_dir);
678 		strcat(ZSTR_VAL(newpath), ZSTR_VAL(pubkey)+1);
679 		zend_string_release(pubkey);
680 		pubkey = newpath;
681 	}
682 	if (ZSTR_LEN(privkey) >= 2 && *(ZSTR_VAL(privkey)) == '~' && *(ZSTR_VAL(privkey)+1) == '/') {
683 		newpath = zend_string_alloc(strlen(pws->pw_dir) + ZSTR_LEN(privkey), 0);
684 		strcpy(ZSTR_VAL(newpath), pws->pw_dir);
685 		strcat(ZSTR_VAL(newpath), ZSTR_VAL(privkey)+1);
686 		zend_string_release(privkey);
687 		privkey = newpath;
688 	}
689 #endif
690 
691 	/* TODO: Support passphrase callback */
692 	if (libssh2_userauth_publickey_fromfile_ex(session, ZSTR_VAL(username), ZSTR_LEN(username), ZSTR_VAL(pubkey), ZSTR_VAL(privkey), ZSTR_VAL(passphrase))) {
693 		char *buf;
694 		int len;
695 		libssh2_session_last_error(session, &buf, &len, 0);
696 		php_error_docref(NULL, E_WARNING, "Authentication failed for %s using public key: %s", ZSTR_VAL(username), buf);
697 		RETURN_FALSE;
698 	}
699 
700 	RETURN_TRUE;
701 }
702 /* }}} */
703 
704 /* {{{ proto bool ssh2_auth_hostbased_file(resource session, string username, string hostname, string pubkeyfile, string privkeyfile[, string passphrase[, string local_username]])
705  * Authenticate using a hostkey
706  */
PHP_FUNCTION(ssh2_auth_hostbased_file)707 PHP_FUNCTION(ssh2_auth_hostbased_file)
708 {
709 	LIBSSH2_SESSION *session;
710 	zval *zsession;
711 	char *username, *hostname, *pubkey, *privkey, *passphrase = NULL, *local_username = NULL;
712 	size_t username_len, hostname_len, pubkey_len, privkey_len, passphrase_len, local_username_len;
713 
714 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rssss|s!s!", &zsession,	&username, &username_len,
715 																					&hostname, &hostname_len,
716 																					&pubkey, &pubkey_len,
717 																					&privkey, &privkey_len,
718 																					&passphrase, &passphrase_len,
719 																					&local_username, &local_username_len) == FAILURE) {
720 		return;
721 	}
722 
723 	if (php_check_open_basedir(pubkey) || php_check_open_basedir(privkey)) {
724 		RETURN_FALSE;
725 	}
726 
727 	SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
728 
729 	if (!local_username) {
730 		local_username = username;
731 		local_username_len = username_len;
732 	}
733 
734 	/* TODO: Support passphrase callback */
735 	if (libssh2_userauth_hostbased_fromfile_ex(session, username, username_len, pubkey, privkey, passphrase, hostname, hostname_len, local_username, local_username_len)) {
736 		php_error_docref(NULL, E_WARNING, "Authentication failed for %s using hostbased public key", username);
737 		RETURN_FALSE;
738 	}
739 
740 	RETURN_TRUE;
741 }
742 /* }}} */
743 
744 /* {{{ proto resource ssh2_forward_listen(resource session, int port[, string host[, long max_connections]])
745  * Bind a port on the remote server and listen for connections
746  */
PHP_FUNCTION(ssh2_forward_listen)747 PHP_FUNCTION(ssh2_forward_listen)
748 {
749 	zval *zsession;
750 	LIBSSH2_SESSION *session;
751 	LIBSSH2_LISTENER *listener;
752 	php_ssh2_listener_data *data;
753 	zend_long port;
754 	char *host = NULL;
755 	size_t host_len;
756 	zend_long max_connections = PHP_SSH2_LISTEN_MAX_QUEUED;
757 
758 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rl|sl", &zsession, &port, &host, &host_len, &max_connections) == FAILURE) {
759 		return;
760 	}
761 
762 	SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
763 
764 	listener = libssh2_channel_forward_listen_ex(session, host, port, NULL, max_connections);
765 
766 	if (!listener) {
767 		php_error_docref(NULL, E_WARNING, "Failure listening on remote port");
768 		RETURN_FALSE;
769 	}
770 
771 	data = emalloc(sizeof(php_ssh2_listener_data));
772 	data->session = session;
773 	data->session_rsrc = Z_RES_P(zsession);
774 	Z_ADDREF_P(zsession);
775 	data->listener = listener;
776 
777 	RETURN_RES(zend_register_resource(data, le_ssh2_listener));
778 }
779 /* }}} */
780 
781 /* {{{ proto stream ssh2_forward_accept(resource listener[, string &shost[, long &sport]])
782  * Accept a connection created by a listener
783  */
PHP_FUNCTION(ssh2_forward_accept)784 PHP_FUNCTION(ssh2_forward_accept)
785 {
786 	zval *zlistener;
787 	php_ssh2_listener_data *data;
788 	LIBSSH2_CHANNEL *channel;
789 	php_ssh2_channel_data *channel_data;
790 	php_stream *stream;
791 
792 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zlistener) == FAILURE) {
793 		return;
794 	}
795 
796 	if ((data = (php_ssh2_listener_data *)zend_fetch_resource(Z_RES_P(zlistener), PHP_SSH2_LISTENER_RES_NAME, le_ssh2_listener)) == NULL) {
797 		RETURN_FALSE;
798 	}
799 
800 	channel = libssh2_channel_forward_accept(data->listener);
801 
802 	if (!channel) {
803 		RETURN_FALSE;
804 	}
805 
806 	channel_data = emalloc(sizeof(php_ssh2_channel_data));
807 	channel_data->channel = channel;
808 	channel_data->streamid = 0;
809 	channel_data->is_blocking = 0;
810 	channel_data->session_rsrc = data->session_rsrc;
811 	channel_data->refcount = NULL;
812 
813 	stream = php_stream_alloc(&php_ssh2_channel_stream_ops, channel_data, 0, "r+");
814 	if (!stream) {
815 		php_error_docref(NULL, E_WARNING, "Failure allocating stream");
816 		efree(channel_data);
817 		libssh2_channel_free(channel);
818 		RETURN_FALSE;
819 	}
820 
821 #if PHP_VERSION_ID < 70300
822 	GC_REFCOUNT(channel_data->session_rsrc)++;
823 #else
824 	GC_ADDREF(channel_data->session_rsrc);
825 #endif
826 
827 	php_stream_to_zval(stream, return_value);
828 }
829 /* }}} */
830 
831 /* {{{ proto int ssh2_poll(array &polldes[, int timeout])
832  * Poll the channels/listeners/streams for events
833  * Returns number of descriptors which returned non-zero revents
834  * Input array should be of the form:
835  * array(
836  *   0 => array(
837  *     [resource] => $channel,$listener, or $stream
838  *     [events] => SSH2_POLL* flags bitwise ORed together
839  *   ),
840  *   1 => ...
841  * )
842  * Each subarray will be populated with an revents element on return
843  */
PHP_FUNCTION(ssh2_poll)844 PHP_FUNCTION(ssh2_poll)
845 {
846 	zval *zdesc, *subarray, **pollmap;
847 	zend_long timeout = PHP_SSH2_DEFAULT_POLL_TIMEOUT;
848 	LIBSSH2_POLLFD *pollfds;
849 	int numfds, i = 0, fds_ready;
850 	int le_stream = php_file_le_stream();
851 	int le_pstream = php_file_le_pstream();
852 	zend_string *hash_key_zstring;
853 
854 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "a|l", &zdesc, &timeout) == FAILURE) {
855 		return;
856 	}
857 
858 	numfds = zend_hash_num_elements(Z_ARRVAL_P(zdesc));
859 	pollfds = safe_emalloc(sizeof(LIBSSH2_POLLFD), numfds, 0);
860 	pollmap = safe_emalloc(sizeof(zval**), numfds, 0);
861 
862 	for(zend_hash_internal_pointer_reset(Z_ARRVAL_P(zdesc));
863 		(subarray = zend_hash_get_current_data(Z_ARRVAL_P(zdesc))) != NULL;
864 		zend_hash_move_forward(Z_ARRVAL_P(zdesc))) {
865 		zval *tmpzval;
866 		int res_type = 0;
867 		void *res;
868 
869 		if (Z_TYPE_P(subarray) != IS_ARRAY) {
870 			php_error_docref(NULL, E_WARNING, "Invalid element in poll array, not a sub array");
871 			numfds--;
872 			continue;
873 		}
874 
875 		hash_key_zstring = zend_string_init("events", sizeof("events") - 1, 0);
876 		if ((tmpzval = zend_hash_find(Z_ARRVAL_P(subarray), hash_key_zstring)) == NULL || Z_TYPE_P(tmpzval) != IS_LONG) {
877 			php_error_docref(NULL, E_WARNING, "Invalid data in subarray, no events element, or not a bitmask");
878 			numfds--;
879 			zend_string_release(hash_key_zstring);
880 			continue;
881 		}
882 		zend_string_release(hash_key_zstring);
883 
884 		pollfds[i].events = Z_LVAL_P(tmpzval);
885 		hash_key_zstring = zend_string_init("resource", sizeof("resource") - 1, 0);
886 		if ((tmpzval = zend_hash_find(Z_ARRVAL_P(subarray), hash_key_zstring)) == NULL || Z_TYPE_P(tmpzval) != IS_REFERENCE
887 			|| (tmpzval = Z_REFVAL_P(tmpzval)) == NULL || Z_TYPE_P(tmpzval) != IS_RESOURCE) {
888 			php_error_docref(NULL, E_WARNING, "Invalid data in subarray, no resource element, or not of type resource");
889 			numfds--;
890 			zend_string_release(hash_key_zstring);
891 			continue;
892 		}
893 		zend_string_release(hash_key_zstring);
894 
895 		res_type = Z_RES_P(tmpzval)->type;
896 		res = zend_fetch_resource_ex(tmpzval, "Poll Resource", res_type);
897 		if (res_type == le_ssh2_listener) {
898 			pollfds[i].type = LIBSSH2_POLLFD_LISTENER;
899 			pollfds[i].fd.listener = ((php_ssh2_listener_data*)res)->listener;
900 		} else if ((res_type == le_stream || res_type == le_pstream) &&
901 				   ((php_stream*)res)->ops == &php_ssh2_channel_stream_ops) {
902 			pollfds[i].type = LIBSSH2_POLLFD_CHANNEL;
903 			pollfds[i].fd.channel = ((php_ssh2_channel_data*)(((php_stream*)res)->abstract))->channel;
904 			/* TODO: Add the ability to select against other stream types */
905 		} else {
906 			php_error_docref(NULL, E_WARNING, "Invalid resource type in subarray: %s", zend_rsrc_list_get_rsrc_type(Z_RES_P(tmpzval)));
907 			numfds--;
908 			continue;
909 		}
910 		pollmap[i] = subarray;
911 		i++;
912 	}
913 
914 	fds_ready = libssh2_poll(pollfds, numfds, timeout * 1000);
915 
916 	for(i = 0; i < numfds; i++) {
917 		zval *subarray = pollmap[i];
918 
919 		if (Z_REFCOUNT_P(subarray) > 1) {
920 			/* Make a new copy of the subarray zval* */
921 			zval new_subarray;
922 			ZVAL_DUP(&new_subarray, subarray);
923 
924 			/* Decrement refcount prior to swapping in new allocation */
925 			Z_DELREF_P(subarray);
926 			*pollmap[i] = new_subarray;
927 		}
928 		hash_key_zstring = zend_string_init("revents", sizeof("revents") - 1, 0);
929 		zend_hash_del(Z_ARRVAL_P(subarray), hash_key_zstring);
930 		zend_string_release(hash_key_zstring);
931 
932 		add_assoc_long(subarray, "revents", pollfds[i].revents);
933 
934 	}
935 	efree(pollmap);
936 	efree(pollfds);
937 
938 	RETURN_LONG(fds_ready);
939 }
940 /* }}} */
941 
942 /* ***********************
943    * Publickey Subsystem *
944    *********************** */
945 
946 /* {{{ proto resource ssh2_publickey_init(resource connection)
947 Initialize the publickey subsystem */
PHP_FUNCTION(ssh2_publickey_init)948 PHP_FUNCTION(ssh2_publickey_init)
949 {
950 	zval *zsession;
951 	LIBSSH2_SESSION *session;
952 	LIBSSH2_PUBLICKEY *pkey;
953 	php_ssh2_pkey_subsys_data *data;
954 
955 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) {
956 		return;
957 	}
958 
959 	SSH2_FETCH_AUTHENTICATED_SESSION(session, zsession);
960 
961 	pkey = libssh2_publickey_init(session);
962 
963 	if (!pkey) {
964 		int last_error = 0;
965 		char *error_msg = NULL;
966 
967 		last_error = libssh2_session_last_error(session, &error_msg, NULL, 0);
968 		php_error_docref(NULL, E_WARNING, "Unable to initialize publickey subsystem(%d) %s", last_error, error_msg);
969 		RETURN_FALSE;
970 	}
971 
972 	data = emalloc(sizeof(php_ssh2_pkey_subsys_data));
973 	data->session = session;
974 	data->session_rsrc = Z_RES_P(zsession);
975 	Z_ADDREF_P(zsession);
976 	data->pkey = pkey;
977 
978 	RETURN_RES(zend_register_resource(data, le_ssh2_pkey_subsys));
979 }
980 /* }}} */
981 
982 /* {{{ proto bool ssh2_publickey_add(resource pkey, string algoname, string blob[, bool overwrite=FALSE [,array attributes=NULL]])
983 Add an additional publickey */
PHP_FUNCTION(ssh2_publickey_add)984 PHP_FUNCTION(ssh2_publickey_add)
985 {
986 	zval *zpkey_data, *zattrs = NULL;
987 	php_ssh2_pkey_subsys_data *data;
988 	char *algo, *blob;
989 	size_t algo_len, blob_len;
990 	unsigned long num_attrs = 0;
991 	libssh2_publickey_attribute *attrs = NULL;
992 	zend_bool overwrite = 0;
993 
994 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss|ba", &zpkey_data, &algo, &algo_len, &blob, &blob_len, &overwrite, &zattrs) == FAILURE) {
995 		return;
996 	}
997 
998 	if ((data = (php_ssh2_pkey_subsys_data *)zend_fetch_resource(Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {
999 		RETURN_FALSE;
1000 	}
1001 
1002 	if (zattrs) {
1003 		HashPosition pos;
1004 		zval *attr_val;
1005 		unsigned long current_attr = 0;
1006 
1007 		num_attrs = zend_hash_num_elements(Z_ARRVAL_P(zattrs));
1008 		attrs = safe_emalloc(num_attrs, sizeof(libssh2_publickey_attribute), 0);
1009 
1010 		for(zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(zattrs), &pos);
1011 			(attr_val = zend_hash_get_current_data_ex(Z_ARRVAL_P(zattrs), &pos)) != NULL;
1012 			zend_hash_move_forward_ex(Z_ARRVAL_P(zattrs), &pos)) {
1013 			zend_string *key;
1014 			int type;
1015 			zend_ulong idx;
1016 			zval copyval = *attr_val;
1017 
1018 			type = zend_hash_get_current_key_ex(Z_ARRVAL_P(zattrs), &key, &idx, &pos);
1019 			if (type == HASH_KEY_NON_EXISTENT) {
1020 				/* All but impossible */
1021 				break;
1022 			}
1023 			if (type == HASH_KEY_IS_LONG) {
1024 				/* Malformed, ignore */
1025 				php_error_docref(NULL, E_WARNING, "Malformed attirbute array, contains numeric index");
1026 				num_attrs--;
1027 				continue;
1028 			}
1029 
1030 			if (!key || (key->len == 1 && key->val[0] == '*')) {
1031 				/* Empty key, ignore */
1032 				php_error_docref(NULL, E_WARNING, "Empty attribute key");
1033 				num_attrs--;
1034 				continue;
1035 			}
1036 
1037 			zval_copy_ctor(&copyval);
1038 			// TODO Sean-Der
1039 			//Z_UNSET_ISREF_P(&copyval);
1040 			//Z_SET_REFCOUNT_P(&copyval, 1);
1041 			convert_to_string(&copyval);
1042 
1043 			if (key->val[0] == '*') {
1044 				attrs[current_attr].mandatory = 1;
1045 				attrs[current_attr].name = key->val + 1;
1046 				attrs[current_attr].name_len = key->len - 2;
1047 			} else {
1048 				attrs[current_attr].mandatory = 0;
1049 				attrs[current_attr].name = key->val;
1050 				attrs[current_attr].name_len = key->len - 1;
1051 			}
1052 			attrs[current_attr].value_len = Z_STRLEN(copyval);
1053 			attrs[current_attr].value = Z_STRVAL(copyval);
1054 
1055 			/* copyval deliberately not dtor'd, we're stealing the string */
1056 			current_attr++;
1057 		}
1058 	}
1059 
1060 	if (libssh2_publickey_add_ex(data->pkey, (unsigned char *) algo, algo_len, (unsigned char *) blob, blob_len, overwrite, num_attrs, attrs)) {
1061 		php_error_docref(NULL, E_WARNING, "Unable to add %s key", algo);
1062 		RETVAL_FALSE;
1063 	} else {
1064 		RETVAL_TRUE;
1065 	}
1066 
1067 	if (attrs) {
1068 		unsigned long i;
1069 
1070 		for(i = 0; i < num_attrs; i++) {
1071 			/* name doesn't need freeing */
1072 			// TODO Sean-Der
1073 			//efree(attrs[i].value);
1074 		}
1075 		efree(attrs);
1076 	}
1077 }
1078 /* }}} */
1079 
1080 /* {{{ proto bool ssh2_publickey_remove(resource pkey, string algoname, string blob)
1081 Remove a publickey entry */
PHP_FUNCTION(ssh2_publickey_remove)1082 PHP_FUNCTION(ssh2_publickey_remove)
1083 {
1084 	zval *zpkey_data;
1085 	php_ssh2_pkey_subsys_data *data;
1086 	char *algo, *blob;
1087 	size_t algo_len, blob_len;
1088 
1089 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rss", &zpkey_data, &algo, &algo_len, &blob, &blob_len) == FAILURE) {
1090 		return;
1091 	}
1092 
1093 	if ((data = (php_ssh2_pkey_subsys_data *)zend_fetch_resource(Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {
1094 		RETURN_FALSE;
1095 	}
1096 
1097 	if (libssh2_publickey_remove_ex(data->pkey, (unsigned char *) algo, algo_len, (unsigned char *) blob, blob_len)) {
1098 		php_error_docref(NULL, E_WARNING, "Unable to remove %s key", algo);
1099 		RETURN_FALSE;
1100 	}
1101 
1102 	RETURN_TRUE;
1103 }
1104 /* }}} */
1105 
1106 /* {{{ proto array ssh2_publickey_list(resource pkey)
1107 List currently installed publickey entries */
PHP_FUNCTION(ssh2_publickey_list)1108 PHP_FUNCTION(ssh2_publickey_list)
1109 {
1110 	zval *zpkey_data;
1111 	php_ssh2_pkey_subsys_data *data;
1112 	unsigned long num_keys, i;
1113 	libssh2_publickey_list *keys;
1114 	zend_string *hash_key_zstring;
1115 
1116 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zpkey_data) == FAILURE) {
1117 		return;
1118 	}
1119 
1120 	if ((data = (php_ssh2_pkey_subsys_data *)zend_fetch_resource(Z_RES_P(zpkey_data), PHP_SSH2_PKEY_SUBSYS_RES_NAME, le_ssh2_pkey_subsys)) == NULL) {
1121 		RETURN_FALSE;
1122 	}
1123 
1124 	if (libssh2_publickey_list_fetch(data->pkey, &num_keys, &keys)) {
1125 		php_error_docref(NULL, E_WARNING, "Unable to list keys on remote server");
1126 		RETURN_FALSE;
1127 	}
1128 
1129 	array_init(return_value);
1130 	for(i = 0; i < num_keys; i++) {
1131 		zval key, attrs;
1132 		unsigned long j;
1133 
1134 		array_init(&key);
1135 
1136 		add_assoc_stringl(&key, "name", (char *) keys[i].name, keys[i].name_len);
1137 		add_assoc_stringl(&key, "blob", (char *) keys[i].blob, keys[i].blob_len);
1138 
1139 		array_init(&attrs);
1140 		for(j = 0; j < keys[i].num_attrs; j++) {
1141 			zval attr;
1142 
1143 			ZVAL_STRINGL(&attr, keys[i].attrs[j].value, keys[i].attrs[j].value_len);
1144 			hash_key_zstring = zend_string_init(keys[i].attrs[j].name, keys[i].attrs[j].name_len, 0);
1145 			zend_hash_add(Z_ARRVAL_P(&attrs), hash_key_zstring, &attr);
1146 			zend_string_release(hash_key_zstring);
1147 		}
1148 		add_assoc_zval(&key, "attrs", &attrs);
1149 
1150 		add_next_index_zval(return_value, &key);
1151 	}
1152 
1153 	libssh2_publickey_list_free(data->pkey, keys);
1154 }
1155 /* }}} */
1156 
1157 /* {{{ proto array ssh2_auth_agent(resource session, string username)
1158 Authenticate using the ssh agent */
PHP_FUNCTION(ssh2_auth_agent)1159 PHP_FUNCTION(ssh2_auth_agent)
1160 {
1161 #ifdef PHP_SSH2_AGENT_AUTH
1162 	zval *zsession;
1163 	char *username;
1164 	size_t username_len;
1165 
1166 	LIBSSH2_SESSION *session;
1167 	char *userauthlist;
1168 	LIBSSH2_AGENT *agent = NULL;
1169 	int rc;
1170 	struct libssh2_agent_publickey *identity, *prev_identity = NULL;
1171 
1172 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &zsession, &username, &username_len) == FAILURE) {
1173 		return;
1174 	}
1175 
1176 	SSH2_FETCH_NONAUTHENTICATED_SESSION(session, zsession);
1177 
1178 	/* check what authentication methods are available */
1179 	userauthlist = libssh2_userauth_list(session, username, username_len);
1180 
1181 	if (userauthlist != NULL && strstr(userauthlist, "publickey") == NULL) {
1182 		php_error_docref(NULL, E_WARNING, "\"publickey\" authentication is not supported");
1183 		RETURN_FALSE;
1184 	}
1185 
1186 	/* Connect to the ssh-agent */
1187 	agent = libssh2_agent_init(session);
1188 
1189 	if (!agent) {
1190 		php_error_docref(NULL, E_WARNING, "Failure initializing ssh-agent support");
1191 		RETURN_FALSE;
1192 	}
1193 
1194 	if (libssh2_agent_connect(agent)) {
1195 		php_error_docref(NULL, E_WARNING, "Failure connecting to ssh-agent");
1196 		libssh2_agent_free(agent);
1197 		RETURN_FALSE;
1198 	}
1199 
1200 	if (libssh2_agent_list_identities(agent)) {
1201 		php_error_docref(NULL, E_WARNING, "Failure requesting identities to ssh-agent");
1202 		libssh2_agent_disconnect(agent);
1203 		libssh2_agent_free(agent);
1204 		RETURN_FALSE;
1205 	}
1206 
1207 	while (1) {
1208 		rc = libssh2_agent_get_identity(agent, &identity, prev_identity);
1209 
1210 		if (rc == 1) {
1211 			php_error_docref(NULL, E_WARNING, "Couldn't continue authentication");
1212 			libssh2_agent_disconnect(agent);
1213 			libssh2_agent_free(agent);
1214 			RETURN_FALSE;
1215 		}
1216 
1217 		if (rc < 0) {
1218 			php_error_docref(NULL, E_WARNING, "Failure obtaining identity from ssh-agent support");
1219 			libssh2_agent_disconnect(agent);
1220 			libssh2_agent_free(agent);
1221 			RETURN_FALSE;
1222 		}
1223 
1224 		if (!libssh2_agent_userauth(agent, username, identity)) {
1225 			libssh2_agent_disconnect(agent);
1226 			libssh2_agent_free(agent);
1227 			RETURN_TRUE;
1228 		}
1229 		prev_identity = identity;
1230 	}
1231 #else
1232 	php_error_docref(NULL, E_WARNING, "Upgrade the libssh2 library (needs 1.2.3 or higher) and reinstall the ssh2 extension for ssh2 agent support");
1233 	RETURN_FALSE;
1234 #endif /* PHP_SSH2_AGENT_AUTH */
1235 }
1236 /* }}} */
1237 
1238 /* ***********************
1239    * Module Housekeeping *
1240    *********************** */
1241 
php_ssh2_session_dtor(zend_resource * rsrc)1242 static void php_ssh2_session_dtor(zend_resource *rsrc)
1243 {
1244 	LIBSSH2_SESSION *session = (LIBSSH2_SESSION*)rsrc->ptr;
1245 	php_ssh2_session_data **data = (php_ssh2_session_data**)libssh2_session_abstract(session);
1246 
1247 	libssh2_session_disconnect(session, "PECL/ssh2 (http://pecl.php.net/packages/ssh2)");
1248 
1249 	if (*data) {
1250 		if ((*data)->ignore_cb) {
1251 			zval_ptr_dtor((*data)->ignore_cb);
1252 		}
1253 		if ((*data)->debug_cb) {
1254 			zval_ptr_dtor((*data)->debug_cb);
1255 		}
1256 		if ((*data)->macerror_cb) {
1257 			zval_ptr_dtor((*data)->macerror_cb);
1258 		}
1259 		if ((*data)->disconnect_cb) {
1260 			zval_ptr_dtor((*data)->disconnect_cb);
1261 		}
1262 
1263 		closesocket((*data)->socket);
1264 
1265 		efree(*data);
1266 		*data = NULL;
1267 	}
1268 
1269 	libssh2_session_free(session);
1270 }
1271 
php_ssh2_listener_dtor(zend_resource * rsrc)1272 static void php_ssh2_listener_dtor(zend_resource *rsrc)
1273 {
1274 	php_ssh2_listener_data *data = (php_ssh2_listener_data*)rsrc->ptr;
1275 	LIBSSH2_LISTENER *listener = data->listener;
1276 
1277 	libssh2_channel_forward_cancel(listener);
1278 	zend_list_delete(data->session_rsrc);
1279 	efree(data);
1280 }
1281 
php_ssh2_pkey_subsys_dtor(zend_resource * rsrc)1282 static void php_ssh2_pkey_subsys_dtor(zend_resource *rsrc)
1283 {
1284 	php_ssh2_pkey_subsys_data *data = (php_ssh2_pkey_subsys_data*)rsrc->ptr;
1285 	LIBSSH2_PUBLICKEY *pkey = data->pkey;
1286 
1287 	libssh2_publickey_shutdown(pkey);
1288 	zend_list_delete(data->session_rsrc);
1289 	efree(data);
1290 }
1291 
1292 /* {{{ PHP_MINIT_FUNCTION
1293  */
PHP_MINIT_FUNCTION(ssh2)1294 PHP_MINIT_FUNCTION(ssh2)
1295 {
1296 	le_ssh2_session		= zend_register_list_destructors_ex(php_ssh2_session_dtor, NULL, PHP_SSH2_SESSION_RES_NAME, module_number);
1297 	le_ssh2_listener	= zend_register_list_destructors_ex(php_ssh2_listener_dtor, NULL, PHP_SSH2_LISTENER_RES_NAME, module_number);
1298 	le_ssh2_sftp		= zend_register_list_destructors_ex(php_ssh2_sftp_dtor, NULL, PHP_SSH2_SFTP_RES_NAME, module_number);
1299 	le_ssh2_pkey_subsys	= zend_register_list_destructors_ex(php_ssh2_pkey_subsys_dtor, NULL, PHP_SSH2_PKEY_SUBSYS_RES_NAME, module_number);
1300 
1301 	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_MD5",		PHP_SSH2_FINGERPRINT_MD5,		CONST_CS | CONST_PERSISTENT);
1302 	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_SHA1",		PHP_SSH2_FINGERPRINT_SHA1,		CONST_CS | CONST_PERSISTENT);
1303 	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_HEX",		PHP_SSH2_FINGERPRINT_HEX,		CONST_CS | CONST_PERSISTENT);
1304 	REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_RAW",		PHP_SSH2_FINGERPRINT_RAW,		CONST_CS | CONST_PERSISTENT);
1305 
1306 	REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_CHARS",		PHP_SSH2_TERM_UNIT_CHARS,		CONST_CS | CONST_PERSISTENT);
1307 	REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_PIXELS",		PHP_SSH2_TERM_UNIT_PIXELS,		CONST_CS | CONST_PERSISTENT);
1308 
1309 	REGISTER_STRING_CONSTANT("SSH2_DEFAULT_TERMINAL",	PHP_SSH2_DEFAULT_TERMINAL,		CONST_CS | CONST_PERSISTENT);
1310 	REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_WIDTH",	PHP_SSH2_DEFAULT_TERM_WIDTH,	CONST_CS | CONST_PERSISTENT);
1311 	REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_HEIGHT",	PHP_SSH2_DEFAULT_TERM_HEIGHT,	CONST_CS | CONST_PERSISTENT);
1312 	REGISTER_LONG_CONSTANT("SSH2_DEFAULT_TERM_UNIT",	PHP_SSH2_DEFAULT_TERM_UNIT,		CONST_CS | CONST_PERSISTENT);
1313 
1314 	REGISTER_LONG_CONSTANT("SSH2_STREAM_STDIO",			0,								CONST_CS | CONST_PERSISTENT);
1315 	REGISTER_LONG_CONSTANT("SSH2_STREAM_STDERR",		SSH_EXTENDED_DATA_STDERR,		CONST_CS | CONST_PERSISTENT);
1316 
1317 	/* events/revents */
1318 	REGISTER_LONG_CONSTANT("SSH2_POLLIN",				LIBSSH2_POLLFD_POLLIN,			CONST_CS | CONST_PERSISTENT);
1319 	REGISTER_LONG_CONSTANT("SSH2_POLLEXT",				LIBSSH2_POLLFD_POLLEXT,			CONST_CS | CONST_PERSISTENT);
1320 	REGISTER_LONG_CONSTANT("SSH2_POLLOUT",				LIBSSH2_POLLFD_POLLOUT,			CONST_CS | CONST_PERSISTENT);
1321 
1322 	/* revents only */
1323 	REGISTER_LONG_CONSTANT("SSH2_POLLERR",				LIBSSH2_POLLFD_POLLERR,			CONST_CS | CONST_PERSISTENT);
1324 	REGISTER_LONG_CONSTANT("SSH2_POLLHUP",				LIBSSH2_POLLFD_POLLHUP,			CONST_CS | CONST_PERSISTENT);
1325 	REGISTER_LONG_CONSTANT("SSH2_POLLNVAL",				LIBSSH2_POLLFD_POLLNVAL,		CONST_CS | CONST_PERSISTENT);
1326 	REGISTER_LONG_CONSTANT("SSH2_POLL_SESSION_CLOSED",	LIBSSH2_POLLFD_SESSION_CLOSED,	CONST_CS | CONST_PERSISTENT);
1327 	REGISTER_LONG_CONSTANT("SSH2_POLL_CHANNEL_CLOSED",	LIBSSH2_POLLFD_CHANNEL_CLOSED,	CONST_CS | CONST_PERSISTENT);
1328 	REGISTER_LONG_CONSTANT("SSH2_POLL_LISTENER_CLOSED",	LIBSSH2_POLLFD_LISTENER_CLOSED,	CONST_CS | CONST_PERSISTENT);
1329 
1330 	return (php_register_url_stream_wrapper("ssh2.shell", &php_ssh2_stream_wrapper_shell) == SUCCESS &&
1331 			php_register_url_stream_wrapper("ssh2.exec", &php_ssh2_stream_wrapper_exec) == SUCCESS &&
1332 			php_register_url_stream_wrapper("ssh2.tunnel", &php_ssh2_stream_wrapper_tunnel) == SUCCESS &&
1333 			php_register_url_stream_wrapper("ssh2.scp", &php_ssh2_stream_wrapper_scp) == SUCCESS &&
1334 			php_register_url_stream_wrapper("ssh2.sftp", &php_ssh2_sftp_wrapper) == SUCCESS) ? SUCCESS : FAILURE;
1335 }
1336 /* }}} */
1337 
1338 /* {{{ PHP_MSHUTDOWN_FUNCTION
1339  */
PHP_MSHUTDOWN_FUNCTION(ssh2)1340 PHP_MSHUTDOWN_FUNCTION(ssh2)
1341 {
1342 	return (php_unregister_url_stream_wrapper("ssh2.shell") == SUCCESS &&
1343 			php_unregister_url_stream_wrapper("ssh2.exec") == SUCCESS &&
1344 			php_unregister_url_stream_wrapper("ssh2.tunnel") == SUCCESS &&
1345 			php_unregister_url_stream_wrapper("ssh2.scp") == SUCCESS &&
1346 			php_unregister_url_stream_wrapper("ssh2.sftp") == SUCCESS) ? SUCCESS : FAILURE;
1347 }
1348 /* }}} */
1349 
1350 /* {{{ PHP_MINFO_FUNCTION
1351  */
PHP_MINFO_FUNCTION(ssh2)1352 PHP_MINFO_FUNCTION(ssh2)
1353 {
1354 	php_info_print_table_start();
1355 	php_info_print_table_header(2, "SSH2 support", "enabled");
1356 	php_info_print_table_row(2, "extension version", PHP_SSH2_VERSION);
1357 	php_info_print_table_row(2, "libssh2 version", LIBSSH2_VERSION);
1358 	php_info_print_table_row(2, "banner", LIBSSH2_SSH_BANNER);
1359 	php_info_print_table_end();
1360 }
1361 /* }}} */
1362 
1363 /* {{{ ZEND_BEGIN_ARG_INFO
1364  */
1365 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_connect, 0, 0, 1)
1366  	ZEND_ARG_INFO(0, host)
1367  	ZEND_ARG_INFO(0, port)
1368  	ZEND_ARG_INFO(0, methods)
1369  	ZEND_ARG_INFO(0, callbacks)
1370 ZEND_END_ARG_INFO()
1371 
1372 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_disconnect, 1)
1373  	ZEND_ARG_INFO(0, session)
1374 ZEND_END_ARG_INFO()
1375 
1376 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_methods_negotiated, 1)
1377  	ZEND_ARG_INFO(0, session)
1378 ZEND_END_ARG_INFO()
1379 
1380 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_fingerprint, 0, 0, 1)
1381  	ZEND_ARG_INFO(0, session)
1382  	ZEND_ARG_INFO(0, flags)
1383 ZEND_END_ARG_INFO()
1384 
1385 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_none, 2)
1386  	ZEND_ARG_INFO(0, session)
1387  	ZEND_ARG_INFO(0, username)
1388 ZEND_END_ARG_INFO()
1389 
1390 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_password, 3)
1391  	ZEND_ARG_INFO(0, session)
1392  	ZEND_ARG_INFO(0, username)
1393  	ZEND_ARG_INFO(0, password)
1394 ZEND_END_ARG_INFO()
1395 
1396 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_pubkey_file, 0, 0, 4)
1397  	ZEND_ARG_INFO(0, session)
1398  	ZEND_ARG_INFO(0, username)
1399  	ZEND_ARG_INFO(0, pubkeyfile)
1400  	ZEND_ARG_INFO(0, privkeyfile)
1401  	ZEND_ARG_INFO(0, passphrase)
1402 ZEND_END_ARG_INFO()
1403 
1404 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_auth_hostbased_file, 0, 0, 5)
1405  	ZEND_ARG_INFO(0, session)
1406  	ZEND_ARG_INFO(0, username)
1407  	ZEND_ARG_INFO(0, hostname)
1408  	ZEND_ARG_INFO(0, pubkeyfile)
1409  	ZEND_ARG_INFO(0, privkeyfile)
1410  	ZEND_ARG_INFO(0, passphrase)
1411  	ZEND_ARG_INFO(0, local_username)
1412 ZEND_END_ARG_INFO()
1413 
1414 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_forward_listen, 0, 0, 2)
1415  	ZEND_ARG_INFO(0, session)
1416  	ZEND_ARG_INFO(0, port)
1417  	ZEND_ARG_INFO(0, host)
1418  	ZEND_ARG_INFO(0, max_connections)
1419 ZEND_END_ARG_INFO()
1420 
1421 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_forward_accept, 0, 0, 1)
1422  	ZEND_ARG_INFO(0, listener)
1423  	ZEND_ARG_INFO(1, host)
1424  	ZEND_ARG_INFO(0, port)
1425 ZEND_END_ARG_INFO()
1426 
1427 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_shell, 0, 0, 1)
1428  	ZEND_ARG_INFO(0, session)
1429  	ZEND_ARG_INFO(0, termtype)
1430  	ZEND_ARG_INFO(0, env)
1431  	ZEND_ARG_INFO(0, width)
1432  	ZEND_ARG_INFO(0, height)
1433  	ZEND_ARG_INFO(0, width_height_type)
1434 ZEND_END_ARG_INFO()
1435 
1436 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_exec, 0, 0, 2)
1437  	ZEND_ARG_INFO(0, session)
1438  	ZEND_ARG_INFO(0, command)
1439  	ZEND_ARG_INFO(0, pty)
1440  	ZEND_ARG_INFO(0, env)
1441  	ZEND_ARG_INFO(0, width)
1442  	ZEND_ARG_INFO(0, height)
1443  	ZEND_ARG_INFO(0, width_height_type)
1444 ZEND_END_ARG_INFO()
1445 
1446 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_tunnel, 3)
1447  	ZEND_ARG_INFO(0, session)
1448  	ZEND_ARG_INFO(0, host)
1449  	ZEND_ARG_INFO(0, port)
1450 ZEND_END_ARG_INFO()
1451 
1452 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_scp_recv, 3)
1453  	ZEND_ARG_INFO(0, session)
1454  	ZEND_ARG_INFO(0, remote_file)
1455  	ZEND_ARG_INFO(0, local_file)
1456 ZEND_END_ARG_INFO()
1457 
1458 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_scp_send, 0, 0, 3)
1459  	ZEND_ARG_INFO(0, session)
1460  	ZEND_ARG_INFO(0, local_file)
1461  	ZEND_ARG_INFO(0, remote_file)
1462  	ZEND_ARG_INFO(0, create_mode)
1463 ZEND_END_ARG_INFO()
1464 
1465 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_fetch_stream, 2)
1466  	ZEND_ARG_INFO(0, channel)
1467  	ZEND_ARG_INFO(0, streamid)
1468 ZEND_END_ARG_INFO()
1469 
1470 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_poll, 0, 0, 1)
1471  	ZEND_ARG_INFO(1, polldes)
1472  	ZEND_ARG_INFO(0, timeout)
1473 ZEND_END_ARG_INFO()
1474 
1475 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_send_eof, 0, 0, 1)
1476 	ZEND_ARG_INFO(0, channel)
1477 ZEND_END_ARG_INFO()
1478 
1479 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp, 1)
1480  	ZEND_ARG_INFO(0, session)
1481 ZEND_END_ARG_INFO()
1482 
1483 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_rename, 3)
1484  	ZEND_ARG_INFO(0, sftp)
1485  	ZEND_ARG_INFO(0, from)
1486  	ZEND_ARG_INFO(0, to)
1487 ZEND_END_ARG_INFO()
1488 
1489 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_unlink, 2)
1490  	ZEND_ARG_INFO(0, sftp)
1491  	ZEND_ARG_INFO(0, filename)
1492 ZEND_END_ARG_INFO()
1493 
1494 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_sftp_mkdir, 0, 0, 2)
1495  	ZEND_ARG_INFO(0, sftp)
1496  	ZEND_ARG_INFO(0, dirname)
1497  	ZEND_ARG_INFO(0, mode)
1498  	ZEND_ARG_INFO(0, recursive)
1499 ZEND_END_ARG_INFO()
1500 
1501 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_rmdir, 2)
1502  	ZEND_ARG_INFO(0, sftp)
1503  	ZEND_ARG_INFO(0, dirname)
1504 ZEND_END_ARG_INFO()
1505 
1506 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_chmod, 3)
1507  	ZEND_ARG_INFO(0, sftp)
1508  	ZEND_ARG_INFO(0, filename)
1509  	ZEND_ARG_INFO(0, mode)
1510 ZEND_END_ARG_INFO()
1511 
1512 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_stat, 2)
1513  	ZEND_ARG_INFO(0, sftp)
1514  	ZEND_ARG_INFO(0, path)
1515 ZEND_END_ARG_INFO()
1516 
1517 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_lstat, 2)
1518  	ZEND_ARG_INFO(0, sftp)
1519  	ZEND_ARG_INFO(0, path)
1520 ZEND_END_ARG_INFO()
1521 
1522 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_symlink, 3)
1523  	ZEND_ARG_INFO(0, sftp)
1524  	ZEND_ARG_INFO(0, target)
1525  	ZEND_ARG_INFO(0, link)
1526 ZEND_END_ARG_INFO()
1527 
1528 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_readlink, 2)
1529  	ZEND_ARG_INFO(0, sftp)
1530  	ZEND_ARG_INFO(0, link)
1531 ZEND_END_ARG_INFO()
1532 
1533 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_sftp_realpath, 2)
1534  	ZEND_ARG_INFO(0, sftp)
1535  	ZEND_ARG_INFO(0, filename)
1536 ZEND_END_ARG_INFO()
1537 
1538 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_init, 1)
1539  	ZEND_ARG_INFO(0, session)
1540 ZEND_END_ARG_INFO()
1541 
1542 ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_publickey_add, 0, 0, 3)
1543  	ZEND_ARG_INFO(0, pkey)
1544  	ZEND_ARG_INFO(0, algoname)
1545  	ZEND_ARG_INFO(0, blob)
1546  	ZEND_ARG_INFO(0, overwrite)
1547  	ZEND_ARG_INFO(0, attributes)
1548 ZEND_END_ARG_INFO()
1549 
1550 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_remove, 3)
1551  	ZEND_ARG_INFO(0, pkey)
1552  	ZEND_ARG_INFO(0, algoname)
1553  	ZEND_ARG_INFO(0, blob)
1554 ZEND_END_ARG_INFO()
1555 
1556 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_publickey_list, 1)
1557  	ZEND_ARG_INFO(0, pkey)
1558 ZEND_END_ARG_INFO()
1559 
1560 ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_agent, 2)
1561  	ZEND_ARG_INFO(0, session)
1562  	ZEND_ARG_INFO(0, username)
1563 ZEND_END_ARG_INFO()
1564 /* }}} */
1565 
1566 /* {{{ ssh2_functions[]
1567  */
1568 zend_function_entry ssh2_functions[] = {
1569 	PHP_FE(ssh2_connect,						arginfo_ssh2_connect)
1570 	PHP_FE(ssh2_disconnect,						arginfo_ssh2_disconnect)
1571 	PHP_FE(ssh2_methods_negotiated,				arginfo_ssh2_methods_negotiated)
1572 	PHP_FE(ssh2_fingerprint,					arginfo_ssh2_fingerprint)
1573 
1574 	PHP_FE(ssh2_auth_none,						arginfo_ssh2_auth_none)
1575 	PHP_FE(ssh2_auth_password,					arginfo_ssh2_auth_password)
1576 	PHP_FE(ssh2_auth_pubkey_file,				arginfo_ssh2_auth_pubkey_file)
1577 	PHP_FE(ssh2_auth_hostbased_file,			arginfo_ssh2_auth_hostbased_file)
1578 
1579 	PHP_FE(ssh2_forward_listen,					arginfo_ssh2_forward_listen)
1580 	PHP_FE(ssh2_forward_accept,					arginfo_ssh2_forward_accept)
1581 
1582 	/* Stream Stuff */
1583 	PHP_FE(ssh2_shell,							arginfo_ssh2_shell)
1584 	PHP_FE(ssh2_exec,							arginfo_ssh2_exec)
1585 	PHP_FE(ssh2_tunnel,							arginfo_ssh2_tunnel)
1586 	PHP_FE(ssh2_scp_recv,						arginfo_ssh2_scp_recv)
1587 	PHP_FE(ssh2_scp_send,						arginfo_ssh2_scp_send)
1588 	PHP_FE(ssh2_fetch_stream,					arginfo_ssh2_fetch_stream)
1589 	PHP_FE(ssh2_poll,							arginfo_ssh2_poll)
1590 	PHP_FE(ssh2_send_eof,						arginfo_ssh2_send_eof)
1591 
1592 	/* SFTP Stuff */
1593 	PHP_FE(ssh2_sftp,							arginfo_ssh2_sftp)
1594 
1595 	/* SFTP Wrapper Ops */
1596 	PHP_FE(ssh2_sftp_rename,					arginfo_ssh2_sftp_rename)
1597 	PHP_FE(ssh2_sftp_unlink,					arginfo_ssh2_sftp_unlink)
1598 	PHP_FE(ssh2_sftp_mkdir,						arginfo_ssh2_sftp_mkdir)
1599 	PHP_FE(ssh2_sftp_rmdir,						arginfo_ssh2_sftp_rmdir)
1600 	PHP_FE(ssh2_sftp_chmod,						arginfo_ssh2_sftp_chmod)
1601 	PHP_FE(ssh2_sftp_stat,						arginfo_ssh2_sftp_stat)
1602 	PHP_FE(ssh2_sftp_lstat,						arginfo_ssh2_sftp_lstat)
1603 	PHP_FE(ssh2_sftp_symlink,					arginfo_ssh2_sftp_symlink)
1604 	PHP_FE(ssh2_sftp_readlink,					arginfo_ssh2_sftp_readlink)
1605 	PHP_FE(ssh2_sftp_realpath,					arginfo_ssh2_sftp_realpath)
1606 
1607 	/* Publickey subsystem */
1608 	PHP_FE(ssh2_publickey_init,					arginfo_ssh2_publickey_init)
1609 	PHP_FE(ssh2_publickey_add,					arginfo_ssh2_publickey_add)
1610 	PHP_FE(ssh2_publickey_remove,				arginfo_ssh2_publickey_remove)
1611 	PHP_FE(ssh2_publickey_list,					arginfo_ssh2_publickey_list)
1612 
1613 	PHP_FE(ssh2_auth_agent,						arginfo_ssh2_auth_agent)
1614 
1615 	{NULL, NULL, NULL}
1616 };
1617 /* }}} */
1618 
1619 /* {{{ ssh2_module_entry
1620  */
1621 zend_module_entry ssh2_module_entry = {
1622 	STANDARD_MODULE_HEADER,
1623 	"ssh2",
1624 	ssh2_functions,
1625 	PHP_MINIT(ssh2),
1626 	PHP_MSHUTDOWN(ssh2),
1627 	NULL, /* RINIT */
1628 	NULL, /* RSHUTDOWN */
1629 	PHP_MINFO(ssh2),
1630 	PHP_SSH2_VERSION,
1631 	STANDARD_MODULE_PROPERTIES
1632 };
1633 /* }}} */
1634 
1635 #ifdef COMPILE_DL_SSH2
1636 ZEND_GET_MODULE(ssh2)
1637 #endif
1638 
1639 /*
1640  * Local variables:
1641  * tab-width: 4
1642  * c-basic-offset: 4
1643  * End:
1644  * vim600: noet sw=4 ts=4 fdm=marker
1645  * vim<600: noet sw=4 ts=4
1646  */
1647