Skip to content

Commit 1ab97b3

Browse files
committed
PHPC-191 && PHPC-192: Add verify_expiry && verify_peer_name certificate verify options
1 parent ee9e331 commit 1ab97b3

File tree

6 files changed

+398
-0
lines changed

6 files changed

+398
-0
lines changed

bin/prep-release.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ function get_files() {
8080

8181
"src/MongoDB/*.{c,h}",
8282
"src/BSON/*.{c,h}",
83+
"src/contrib/*.{c,h}",
8384

8485
"src/libbson/build/autotools/*.{m4}",
8586
"src/libbson/build/autotools/m4/*.{m4}",

config.m4

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,9 @@ if test "$MONGODB" != "no"; then
146146
MONGODB_ROOT="\
147147
php_phongo.c \
148148
";
149+
MONGODB_CONTRIB="\
150+
src/contrib/php-ssl.c \
151+
";
149152
MONGODB_MONGODB_CLASSES="\
150153
src/MongoDB/Command.c \
151154
src/MongoDB/Cursor.c \
@@ -273,11 +276,13 @@ MONGOC_SOURCES_SASL=mongoc-sasl.c
273276
PHP_ADD_SOURCES(PHP_EXT_DIR(mongodb), $MONGODB_BSON_CLASSES)
274277
PHP_ADD_SOURCES(PHP_EXT_DIR(mongodb), $MONGODB_MONGODB_CLASSES)
275278
PHP_ADD_SOURCES(PHP_EXT_DIR(mongodb), $MONGODB_MONGODB_EXCEPTIONS)
279+
PHP_ADD_SOURCES(PHP_EXT_DIR(mongodb), $MONGODB_CONTRIB)
276280
else
277281
PHP_ADD_SOURCES_X(PHP_EXT_DIR(mongodb), $MONGODB_BSON, [$STD_CFLAGS $MAINTAINER_CFLAGS $COVERAGE_CFLAGS], shared_objects_mongodb, yes)
278282
PHP_ADD_SOURCES_X(PHP_EXT_DIR(mongodb), $MONGODB_BSON_CLASSES, [$STD_CFLAGS $MAINTAINER_CFLAGS $COVERAGE_CFLAGS], shared_objects_mongodb, yes)
279283
PHP_ADD_SOURCES_X(PHP_EXT_DIR(mongodb), $MONGODB_MONGODB_CLASSES, [$STD_CFLAGS $MAINTAINER_CFLAGS $COVERAGE_CFLAGS], shared_objects_mongodb, yes)
280284
PHP_ADD_SOURCES_X(PHP_EXT_DIR(mongodb), $MONGODB_MONGODB_EXCEPTIONS, [$STD_CFLAGS $MAINTAINER_CFLAGS $COVERAGE_CFLAGS], shared_objects_mongodb, yes)
285+
PHP_ADD_SOURCES_X(PHP_EXT_DIR(mongodb), $MONGODB_CONTRIB, [$STD_CFLAGS $MAINTAINER_CFLAGS $COVERAGE_CFLAGS], shared_objects_mongodb, yes)
281286
fi
282287

283288
dnl libmongoc stuff {{{

config.w32

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ if (PHP_MONGODB != "no") {
1111
ADD_SOURCES(configure_module_dirname + "/src/BSON", "Type.c Unserializable.c Serializable.c Persistable.c Binary.c Javascript.c MaxKey.c MinKey.c ObjectID.c Regex.c Timestamp.c UTCDatetime.c", "mongodb");
1212
ADD_SOURCES(configure_module_dirname + "/src/MongoDB", "Command.c Cursor.c CursorId.c Manager.c Query.c ReadPreference.c Result.c Server.c BulkWrite.c WriteConcern.c WriteConcernError.c WriteError.c WriteResult.c", "mongodb");
1313
ADD_SOURCES(configure_module_dirname + "/src/MongoDB", "Exception.c RuntimeException.c InvalidArgumentException.c ConnectionException.c AuthenticationException.c SSLConnectionException.c DuplicateKeyException.c ExecutionTimeoutException.c ConnectionTimeoutException.c WriteException.c WriteConcernException.c BulkWriteException.c", "mongodb");
14+
ADD_SOURCES(configure_module_dirname + "/src/contrib/", "php-ssl.c", "mongodb");
1415
ADD_SOURCES(configure_module_dirname + "/src/libbson/src/yajl", "yajl_version.c yajl.c yajl_encode.c yajl_lex.c yajl_parser.c yajl_buf.c yajl_tree.c yajl_alloc.c yajl_gen.c", "mongodb");
1516
ADD_SOURCES(configure_module_dirname + "/src/libbson/src/bson", "bcon.c bson.c bson-atomic.c bson-clock.c bson-context.c bson-error.c bson-iter.c bson-iso8601.c bson-json.c bson-keys.c bson-md5.c bson-memory.c bson-oid.c bson-reader.c bson-string.c bson-timegm.c bson-utf8.c bson-value.c bson-version.c bson-writer.c", "mongodb");
1617
ADD_SOURCES(configure_module_dirname + "/src/libmongoc/src/mongoc", "mongoc-array.c mongoc-async.c mongoc-async-cmd.c mongoc-buffer.c mongoc-bulk-operation.c mongoc-b64.c mongoc-client.c mongoc-client-pool.c mongoc-cluster.c mongoc-collection.c mongoc-counters.c mongoc-cursor.c mongoc-cursor-array.c mongoc-cursor-transform.c mongoc-cursor-cursorid.c mongoc-database.c mongoc-init.c mongoc-gridfs.c mongoc-gridfs-file.c mongoc-gridfs-file-page.c mongoc-gridfs-file-list.c mongoc-index.c mongoc-list.c mongoc-log.c mongoc-matcher-op.c mongoc-matcher.c mongoc-opcode.c mongoc-queue.c mongoc-read-prefs.c mongoc-rpc.c mongoc-set.c mongoc-server-description.c mongoc-socket.c mongoc-stream.c mongoc-stream-buffered.c mongoc-stream-file.c mongoc-stream-gridfs.c mongoc-stream-socket.c mongoc-topology.c mongoc-topology-scanner.c mongoc-topology-description.c mongoc-uri.c mongoc-util.c mongoc-write-command.c mongoc-write-concern.c", "mongodb");

php_phongo.c

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
#include "php_phongo.h"
5656
#include "php_bson.h"
5757
#include "php_array.h"
58+
#include "src/contrib/php-ssl.h"
5859

5960
#undef MONGOC_LOG_DOMAIN
6061
#define MONGOC_LOG_DOMAIN "PHONGO"
@@ -796,6 +797,97 @@ ssize_t phongo_stream_poll (mongoc_stream_poll_t *streams, size_t nstreams, int3
796797
return rval;
797798
} /* }}} */
798799

800+
#if PHP_VERSION_ID < 50600
801+
int php_mongo_verify_hostname(const char *hostname, X509 *cert TSRMLS_DC)
802+
{
803+
if (php_mongo_matches_san_list(cert, hostname) == SUCCESS) {
804+
return SUCCESS;
805+
}
806+
807+
if (php_mongo_matches_common_name(cert, hostname TSRMLS_CC) == SUCCESS) {
808+
return SUCCESS;
809+
}
810+
811+
return FAILURE;
812+
}
813+
814+
int php_phongo_peer_verify(php_stream *stream, X509 *cert, const char *hostname, bson_error_t *error TSRMLS_DC)
815+
{
816+
zval **verify_peer_name;
817+
818+
/* This option is available since PHP 5.6.0 */
819+
if (php_stream_context_get_option(stream->context, "ssl", "verify_peer_name", &verify_peer_name) == SUCCESS && zend_is_true(*verify_peer_name)) {
820+
zval **zhost = NULL;
821+
const char *peer;
822+
823+
if (php_stream_context_get_option(stream->context, "ssl", "peer_name", &zhost) == SUCCESS) {
824+
convert_to_string_ex(zhost);
825+
peer = Z_STRVAL_PP(zhost);
826+
} else {
827+
peer = hostname;
828+
}
829+
830+
if (php_mongo_verify_hostname(peer, cert TSRMLS_CC) == FAILURE) {
831+
bson_set_error(error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, "Remote certificate SubjectAltName or CN does not match '%s'", hostname);
832+
return false;
833+
}
834+
}
835+
836+
return true;
837+
}
838+
#endif
839+
840+
bool php_phongo_ssl_verify(php_stream *stream, const char *hostname, bson_error_t *error TSRMLS_DC)
841+
{
842+
zval **zcert;
843+
zval **verify_expiry;
844+
int resource_type;
845+
X509 *cert;
846+
int type;
847+
848+
if (!stream->context) {
849+
return true;
850+
}
851+
852+
if (!(php_stream_context_get_option(stream->context, "ssl", "peer_certificate", &zcert) == SUCCESS && Z_TYPE_PP(zcert) == IS_RESOURCE)) {
853+
bson_set_error(error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, "Could not capture certificate of %s", hostname);
854+
return false;
855+
}
856+
857+
858+
859+
zend_list_find(Z_LVAL_PP(zcert), &resource_type);
860+
cert = (X509 *)zend_fetch_resource(zcert TSRMLS_CC, -1, "OpenSSL X.509", &type, 1, resource_type);
861+
862+
if (!cert) {
863+
bson_set_error(error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, "Could not get certificate of %s", hostname);
864+
return false;
865+
}
866+
867+
#if PHP_VERSION_ID < 50600
868+
if (!php_phongo_peer_verify(stream, cert, hostname, error TSRMLS_CC)) {
869+
return false;
870+
}
871+
#endif
872+
873+
if (php_stream_context_get_option(stream->context, "ssl", "verify_expiry", &verify_expiry) == SUCCESS && zend_is_true(*verify_expiry)) {
874+
time_t current = time(NULL);
875+
time_t valid_from = php_mongo_asn1_time_to_time_t(X509_get_notBefore(cert) TSRMLS_CC);
876+
time_t valid_until = php_mongo_asn1_time_to_time_t(X509_get_notAfter(cert) TSRMLS_CC);
877+
878+
if (valid_from > current) {
879+
bson_set_error(error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, "Certificate is not valid yet on %s", hostname);
880+
return false;
881+
}
882+
if (current > valid_until) {
883+
bson_set_error(error, MONGOC_ERROR_STREAM, MONGOC_ERROR_STREAM_CONNECT, "Certificate has expired on %s", hostname);
884+
return false;
885+
}
886+
}
887+
888+
return true;
889+
}
890+
799891
mongoc_stream_t* phongo_stream_initiator(const mongoc_uri_t *uri, const mongoc_host_list_t *host, void *user_data, bson_error_t *error) /* {{{ */
800892
{
801893
php_phongo_stream_socket *base_stream = NULL;
@@ -880,6 +972,14 @@ mongoc_stream_t* phongo_stream_initiator(const mongoc_uri_t *uri, const mongoc_h
880972
zend_replace_error_handling(EH_THROW, php_phongo_sslconnectionexception_ce, &error_handling TSRMLS_CC);
881973

882974
mongoc_log(MONGOC_LOG_LEVEL_DEBUG, MONGOC_LOG_DOMAIN, "Enabling SSL");
975+
976+
/* Capture the server certificate so we can do further verification */
977+
if (stream->context) {
978+
zval capture;
979+
ZVAL_BOOL(&capture, 1);
980+
php_stream_context_set_option(stream->context, "ssl", "capture_peer_cert", &capture);
981+
}
982+
883983
if (php_stream_xport_crypto_setup(stream, PHONGO_CRYPTO_METHOD, NULL TSRMLS_CC) < 0) {
884984
zend_restore_error_handling(&error_handling TSRMLS_CC);
885985
php_stream_free(stream, PHP_STREAM_FREE_CLOSE_PERSISTENT | PHP_STREAM_FREE_RSRC_DTOR);
@@ -896,11 +996,21 @@ mongoc_stream_t* phongo_stream_initiator(const mongoc_uri_t *uri, const mongoc_h
896996
return NULL;
897997
}
898998

999+
if (!php_phongo_ssl_verify(stream, host->host, error TSRMLS_CC)) {
1000+
zend_restore_error_handling(&error_handling TSRMLS_CC);
1001+
php_stream_pclose(stream);
1002+
efree(dsn);
1003+
return NULL;
1004+
}
1005+
8991006
zend_restore_error_handling(&error_handling TSRMLS_CC);
9001007
}
9011008
efree(dsn);
9021009

9031010

1011+
/* We only need the context really for SSL initialization, safe to remove now */
1012+
php_stream_context_set(stream, NULL);
1013+
9041014
base_stream = ecalloc(1, sizeof(php_phongo_stream_socket));
9051015
base_stream->stream = stream;
9061016
base_stream->uri = uri;

0 commit comments

Comments
 (0)