diff --git a/config.m4 b/config.m4 index 18b8caa..87b43d8 100644 --- a/config.m4 +++ b/config.m4 @@ -25,12 +25,27 @@ if test "$PHP_SSH2" != "no"; then PHP_ADD_INCLUDE($SSH2_DIR/include) + saved_CPPFLAGS=$CPPFLAGS + CPPFLAGS="-I$SSH2_DIR/include $CPPFLAGS" + AC_MSG_CHECKING([for libssh2 version >= 1.9.0]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[ + #include + #if LIBSSH2_VERSION_NUM < 0x010900 + #error libssh2 1.9.0 or newer is required + #endif + ]], [[]])], [ + AC_MSG_RESULT([yes]) + ], [ + AC_MSG_ERROR([libssh2 version >= 1.9.0 not found]) + ]) + CPPFLAGS=$saved_CPPFLAGS + PHP_CHECK_LIBRARY(ssh2,libssh2_session_hostkey, [ PHP_ADD_LIBRARY_WITH_PATH(ssh2, $SSH2_DIR/lib, SSH2_SHARED_LIBADD) AC_DEFINE(HAVE_SSH2LIB,1,[Have libssh2]) ],[ - AC_MSG_ERROR([libssh2 version >= 1.2 not found]) + AC_MSG_ERROR([libssh2 library not found or too old; version 1.9.0 or newer is required]) ],[ -L$SSH2_DIR/lib -lm ]) @@ -39,7 +54,7 @@ if test "$PHP_SSH2" != "no"; then [ AC_DEFINE(PHP_SSH2_AGENT_AUTH, 1, [Have libssh2 with ssh-agent support]) ],[ - AC_MSG_WARN([libssh2 <= 1.2.3, ssh-agent subsystem support not enabled]) + AC_MSG_ERROR([libssh2_agent_init not found; ensure the linked libssh2 matches the 1.9.0 or newer headers]) ],[ -L$SSH2_DIR/lib -lm ]) @@ -48,7 +63,7 @@ if test "$PHP_SSH2" != "no"; then [ AC_DEFINE(PHP_SSH2_SESSION_TIMEOUT, 1, [Have libssh2 with session timeout support]) ],[ - AC_MSG_WARN([libssh2 < 1.2.9, session timeout support not enabled]) + AC_MSG_ERROR([libssh2_session_set_timeout not found; ensure the linked libssh2 matches the 1.9.0 or newer headers]) ],[ -L$SSH2_DIR/lib -lm ]) @@ -57,7 +72,7 @@ if test "$PHP_SSH2" != "no"; then [ AC_DEFINE(PHP_SSH2_KEEPALIVE, 1, [Have libssh2 with keepalive support]) ],[ - AC_MSG_WARN([libssh2 keepalive support not available]) + AC_MSG_ERROR([libssh2_keepalive_config not found; ensure the linked libssh2 matches the 1.9.0 or newer headers]) ],[ -L$SSH2_DIR/lib -lm ]) @@ -66,7 +81,7 @@ if test "$PHP_SSH2" != "no"; then [ AC_DEFINE(PHP_SSH2_CHANNEL_SIGNAL, 1, [Have libssh2 with channel signal support]) ],[ - AC_MSG_WARN([libssh2 < 1.9.0, channel signal support not available]) + AC_MSG_ERROR([libssh2_channel_signal_ex not found; ensure the linked libssh2 matches the 1.9.0 or newer headers]) ],[ -L$SSH2_DIR/lib -lm ]) diff --git a/package.xml b/package.xml index 07c0427..97a9209 100644 --- a/package.xml +++ b/package.xml @@ -75,6 +75,7 @@ + diff --git a/php_ssh2.h b/php_ssh2.h index c27e002..fd2af55 100644 --- a/php_ssh2.h +++ b/php_ssh2.h @@ -24,6 +24,10 @@ #include "ext/standard/url.h" #include "main/php_network.h" +#if LIBSSH2_VERSION_NUM < 0x010900 +#error "The ssh2 extension requires libssh2 1.9.0 or newer" +#endif + #define PHP_SSH2_VERSION "1.5.0" #define PHP_SSH2_DEFAULT_PORT 22 @@ -32,6 +36,7 @@ #define PHP_SSH2_FINGERPRINT_SHA1 0x0001 #define PHP_SSH2_FINGERPRINT_HEX 0x0000 #define PHP_SSH2_FINGERPRINT_RAW 0x0002 +#define PHP_SSH2_FINGERPRINT_SHA256 0x0004 #define PHP_SSH2_TERM_UNIT_CHARS 0x0000 #define PHP_SSH2_TERM_UNIT_PIXELS 0x0001 diff --git a/ssh2.c b/ssh2.c index 0bb7ce6..911b4d7 100644 --- a/ssh2.c +++ b/ssh2.c @@ -39,6 +39,10 @@ #define MD5_DIGEST_LENGTH 16 #endif +#ifndef SHA256_DIGEST_LENGTH +#define SHA256_DIGEST_LENGTH 32 +#endif + /* True global resources - no need for thread safety here */ int le_ssh2_session; int le_ssh2_listener; @@ -591,18 +595,28 @@ PHP_FUNCTION(ssh2_fingerprint) zval *zsession; const char *fingerprint; zend_long flags = 0; - int i, fingerprint_len; + size_t i, fingerprint_len; + int hash_type; if (zend_parse_parameters(ZEND_NUM_ARGS(), "r|l", &zsession, &flags) == FAILURE) { return; } - fingerprint_len = (flags & PHP_SSH2_FINGERPRINT_SHA1) ? SHA_DIGEST_LENGTH : MD5_DIGEST_LENGTH; + if (flags & PHP_SSH2_FINGERPRINT_SHA256) { + fingerprint_len = SHA256_DIGEST_LENGTH; + hash_type = LIBSSH2_HOSTKEY_HASH_SHA256; + } else if (flags & PHP_SSH2_FINGERPRINT_SHA1) { + fingerprint_len = SHA_DIGEST_LENGTH; + hash_type = LIBSSH2_HOSTKEY_HASH_SHA1; + } else { + fingerprint_len = MD5_DIGEST_LENGTH; + hash_type = LIBSSH2_HOSTKEY_HASH_MD5; + } if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { RETURN_FALSE; } - fingerprint = (char*)libssh2_hostkey_hash(session, (flags & PHP_SSH2_FINGERPRINT_SHA1) ? LIBSSH2_HOSTKEY_HASH_SHA1 : LIBSSH2_HOSTKEY_HASH_MD5); + fingerprint = (char*)libssh2_hostkey_hash(session, hash_type); if (!fingerprint) { php_error_docref(NULL, E_WARNING, "Unable to retrieve fingerprint from specified session"); RETURN_FALSE; @@ -631,6 +645,36 @@ PHP_FUNCTION(ssh2_fingerprint) } /* }}} */ +/* {{{ proto array ssh2_hostkey(resource session) + * Returns the server's raw host key and type + */ +PHP_FUNCTION(ssh2_hostkey) +{ + LIBSSH2_SESSION *session; + zval *zsession; + const char *hostkey; + size_t hostkey_len; + int type; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "r", &zsession) == FAILURE) { + return; + } + + if ((session = (LIBSSH2_SESSION *)zend_fetch_resource(Z_RES_P(zsession), PHP_SSH2_SESSION_RES_NAME, le_ssh2_session)) == NULL) { + RETURN_FALSE; + } + + hostkey = libssh2_session_hostkey(session, &hostkey_len, &type); + if (!hostkey) { + RETURN_FALSE; + } + + array_init(return_value); + add_assoc_stringl(return_value, "key", (char *)hostkey, hostkey_len); + add_assoc_long(return_value, "type", type); +} +/* }}} */ + /* {{{ proto array ssh2_auth_none(resource session, string username) * Attempt "none" authentication, returns a list of allowed methods on failed authentication, * false on utter failure, or true on success @@ -1417,6 +1461,15 @@ PHP_MINIT_FUNCTION(ssh2) REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_SHA1", PHP_SSH2_FINGERPRINT_SHA1, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_HEX", PHP_SSH2_FINGERPRINT_HEX, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_RAW", PHP_SSH2_FINGERPRINT_RAW, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_FINGERPRINT_SHA256", PHP_SSH2_FINGERPRINT_SHA256, CONST_CS | CONST_PERSISTENT); + + REGISTER_LONG_CONSTANT("SSH2_HOSTKEY_TYPE_UNKNOWN", LIBSSH2_HOSTKEY_TYPE_UNKNOWN, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_HOSTKEY_TYPE_RSA", LIBSSH2_HOSTKEY_TYPE_RSA, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_HOSTKEY_TYPE_DSS", LIBSSH2_HOSTKEY_TYPE_DSS, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_HOSTKEY_TYPE_ECDSA_256", LIBSSH2_HOSTKEY_TYPE_ECDSA_256, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_HOSTKEY_TYPE_ECDSA_384", LIBSSH2_HOSTKEY_TYPE_ECDSA_384, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_HOSTKEY_TYPE_ECDSA_521", LIBSSH2_HOSTKEY_TYPE_ECDSA_521, CONST_CS | CONST_PERSISTENT); + REGISTER_LONG_CONSTANT("SSH2_HOSTKEY_TYPE_ED25519", LIBSSH2_HOSTKEY_TYPE_ED25519, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_CHARS", PHP_SSH2_TERM_UNIT_CHARS, CONST_CS | CONST_PERSISTENT); REGISTER_LONG_CONSTANT("SSH2_TERM_UNIT_PIXELS", PHP_SSH2_TERM_UNIT_PIXELS, CONST_CS | CONST_PERSISTENT); @@ -1513,6 +1566,10 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ssh2_fingerprint, 0, 0, 1) ZEND_ARG_INFO(0, flags) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_ssh2_hostkey, 1) + ZEND_ARG_INFO(0, session) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO(arginfo_ssh2_auth_none, 2) ZEND_ARG_INFO(0, session) ZEND_ARG_INFO(0, username) @@ -1727,6 +1784,7 @@ zend_function_entry ssh2_functions[] = { #endif PHP_FE(ssh2_methods_negotiated, arginfo_ssh2_methods_negotiated) PHP_FE(ssh2_fingerprint, arginfo_ssh2_fingerprint) + PHP_FE(ssh2_hostkey, arginfo_ssh2_hostkey) PHP_FE(ssh2_auth_none, arginfo_ssh2_auth_none) PHP_FE(ssh2_auth_password, arginfo_ssh2_auth_password) diff --git a/tests/ssh2_hostkey.phpt b/tests/ssh2_hostkey.phpt new file mode 100644 index 0000000..51afefd --- /dev/null +++ b/tests/ssh2_hostkey.phpt @@ -0,0 +1,28 @@ +--TEST-- +Raw host-key metadata and SHA-256 fingerprints describe the same server key +--SKIPIF-- + +--FILE-- + +--EXPECT-- +bool(true) +bool(true) +bool(true) +bool(true) +bool(true) +int(32) +bool(true)