Ruslan Osmanov avatar Ruslan Osmanov committed db2f90f

config.m4: improved with AC_CHECK_LIB calls

Add: EventSslContext's OPT_VERIFY_PEER and OPT_ALLOW_SELF_SIGNED options are now working by means of custom verify callback. This works through attachment of php_event_ssl_context_t data to the SSL custom (ex) data
Add: OpenSSL init in MINIT

Comments (0)

Files changed (13)

 *.log
 *.out
 *.exp
+*.crt
+*.pem
+*.key
 Makefile*
 
 tests/*

classes/buffer_event.c

 				"Event: Failed creating SSL handle");
 		RETURN_FALSE;
 	}
+	/* Attach ectx to ssl for callbacks */
+	SSL_set_ex_data(ssl, php_event_ssl_data_index, ectx);
 
 	bevent = bufferevent_openssl_socket_new(base->base, fd, ssl, state, options);
 	if (bevent == NULL) {

classes/ssl_context.c

 
 /* {{{ Private */
 
+/* {{{ verify_callback */
+static int verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
+{
+	SSL                      *ssl;
+	X509                     *err_cert;
+	int                       ret      = preverify_ok;
+	int                       err;
+	int                       depth;
+	php_event_ssl_context_t  *ectx;
+	zval                    **ppzval   = NULL;
+	HashTable                *ht;
+
+	ssl  = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
+	ectx = (php_event_ssl_context_t *) SSL_get_ex_data(ssl, php_event_ssl_data_index);
+
+	PHP_EVENT_ASSERT(ectx && ectx->ht);
+	ht = ectx->ht;
+
+	err_cert = X509_STORE_CTX_get_current_cert(ctx);
+	err      = X509_STORE_CTX_get_error(ctx);
+	depth    = X509_STORE_CTX_get_error_depth(ctx);
+
+	/* If OPT_ALLOW_SELF_SIGNED is set and is TRUE, ret = 1 */
+	if (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT
+			&& zend_hash_index_find(ht, PHP_EVENT_OPT_ALLOW_SELF_SIGNED,
+				(void **) &ppzval) == SUCCESS
+			&& zval_is_true(*ppzval)) {
+		ret = 1;
+	}
+
+	/* Verify depth, if OPT_VERIFY_DEPTH option is set */
+	if (zend_hash_index_find(ht, PHP_EVENT_OPT_VERIFY_DEPTH,
+				(void **) &ppzval) == SUCCESS) {
+		convert_to_long_ex(ppzval);
+
+		if (depth > Z_LVAL_PP(ppzval)) {
+			ret = 0;
+			X509_STORE_CTX_set_error(ctx, X509_V_ERR_CERT_CHAIN_TOO_LONG);
+		}
+	}
+
+	return ret;
+}
+/* }}} */
+
 /* {{{ passwd_callback */
 static int passwd_callback(char *buf, int num, int verify, void *data)
 {
 				/* Skip */
 				break;
 			case PHP_EVENT_OPT_VERIFY_PEER:
-				/* Skip */
+				if (zval_is_true(*ppzval)) {
+					SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
+				} else {
+					SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
+				}
 				break;
 			case PHP_EVENT_OPT_VERIFY_DEPTH:
 				convert_to_long_ex(ppzval);
 [  --with-event-openssl Include libevent OpenSSL support], yes, no)
 
 PHP_ARG_WITH(openssl-dir, OpenSSL installation prefix,
-[  --with-openssl-dir[=DIR]  Event: openssl installation prefix], yes, no)
+[  --with-openssl-dir[=DIR]  Event: openssl installation prefix], no, no)
 
 PHP_ARG_WITH([event-libevent-dir], [],
-[  --with-event-libevent-dir[=DIR] Event: libevent installation prefix], $PHP_EVENT_CORE, no)
+[  --with-event-libevent-dir[=DIR] Event: libevent installation prefix], no, no)
 
 PHP_ARG_ENABLE(event-debug, Event: debug support,
 [  --enable-event-debug     Enable debug support in event], no, no)
   [AC_MSG_ERROR([need at least PHP 5.4.0])])
   export CPPFLAGS="$OLD_CPPFLAGS"
   dnl }}}
+  
+  dnl {{{ --enable-event-debug
+  if test "$PHP_EVENT_DEBUG" != "no"; then
+    CFLAGS="$CFLAGS -Wall -g -ggdb -O0"
+    AC_DEFINE(PHP_EVENT_DEBUG, 1, [Enable event debug support])
+  else
+    AC_DEFINE(NDEBUG, 1, [With NDEBUG defined assert generates no code])
+  fi
+  dnl }}}
 
-  dnl {{{ Include libevent
-  AC_MSG_CHECKING([for event2/event.h])
+  dnl {{{ Include libevent headers
+  AC_MSG_CHECKING([for include/event2/event.h])
   EVENT_DIR=
   for i in "$PHP_EVENT_CORE" "$PHP_EVENT_LIBEVENT_DIR" /usr/local /usr /opt; do
 	  if test -f "$i/include/event2/event.h"; then
   fi
 	AC_MSG_RESULT([found in $EVENT_DIR])
 
+	PHP_ADD_INCLUDE($EVENT_DIR/include)
+  dnl }}}
+
 	dnl {{{ Check if it's at least libevent 2.0.2-alpha
   export OLD_CPPFLAGS="$CPPFLAGS"
   export CPPFLAGS="$CPPFLAGS $INCLUDES -DHAVE_EV"
   export CPPFLAGS="$OLD_CPPFLAGS"
   dnl }}}
 	
-	PHP_ADD_INCLUDE($EVENT_DIR/include)
-	PHP_ADD_LIBRARY_WITH_PATH(event_core, $EVENT_LIBDIR, EVENT_SHARED_LIBADD)
-
-  dnl FreeBSD
   if test -d $EVENT_DIR/$PHP_LIBDIR/event2; then
+    dnl FreeBSD
     EVENT_LIBS="-L$EVENT_DIR/$PHP_LIBDIR -L$EVENT_DIR/$PHP_LIBDIR/event2"
     EVENT_LIBDIR=$EVENT_DIR/$PHP_LIBDIR/event2
   else
     EVENT_LIBS="-L$EVENT_DIR/$PHP_LIBDIR"
     EVENT_LIBDIR=$EVENT_DIR/$PHP_LIBDIR
   fi
-
   LDFLAGS="$EVENT_LIBS -levent_core $LDFLAGS"
-  dnl }}}
 
-  dnl {{{ --enable-event-debug
-  if test "$PHP_EVENT_DEBUG" != "no"; then
-    CFLAGS="$CFLAGS -Wall -g -ggdb -O0"
-    AC_DEFINE(PHP_EVENT_DEBUG, 1, [Enable event debug support])
-  else
-    AC_DEFINE(NDEBUG, 1, [With NDEBUG defined assert generates no code])
-  fi
-  dnl }}}
-  
+  dnl {{{ event_core
+	AC_CHECK_LIB(event_core, event_free, [
+	  PHP_ADD_LIBRARY_WITH_PATH(event_core, $EVENT_LIBDIR, EVENT_SHARED_LIBADD)
+	], [
+    AC_MSG_ERROR([event_free not found in event_core library, or the library is not installed])
+	])
+
   event_src="php_event.c \
     src/util.c \
     src/fe.c \
     classes/buffer.c \
     classes/buffer_pos.c \
     classes/event_util.c"
+  dnl }}}
 
   dnl {{{ --with-event-extra
   if test "$PHP_EVENT_EXTRA" != "no"; then
-	  PHP_ADD_LIBRARY_WITH_PATH(event_extra, $EVENT_LIBDIR, EVENT_SHARED_LIBADD)
-    LDFLAGS="-levent_extra $LDFLAGS"
-    AC_DEFINE(HAVE_EVENT_EXTRA_LIB, 1, [ ])
+    AC_CHECK_LIB(event_extra, evdns_base_free, [
+	    PHP_ADD_LIBRARY_WITH_PATH(event_extra, $EVENT_LIBDIR, EVENT_SHARED_LIBADD)
+      LDFLAGS="-levent_extra $LDFLAGS"
+      AC_DEFINE(HAVE_EVENT_EXTRA_LIB, 1, [ ])
+    ], [
+      AC_MSG_ERROR([evdns_base_free not found in event_extra library, or the library is not installed])
+    ])
 
     event_src="$event_src \
       classes/dns.c \
     fi
 
     PHP_SETUP_OPENSSL(EVENT_SHARED_LIBADD)
-    PHP_ADD_LIBRARY_WITH_PATH(event_openssl, $EVENT_LIBDIR, EVENT_SHARED_LIBADD)
-    LDFLAGS="-levent_openssl $LDFLAGS"
-    AC_DEFINE(HAVE_EVENT_OPENSSL_LIB, 1, [ ])
+
+    AC_CHECK_LIB(event_openssl, bufferevent_openssl_get_ssl, [
+      PHP_ADD_LIBRARY_WITH_PATH(event_openssl, $EVENT_LIBDIR, EVENT_SHARED_LIBADD)
+      LDFLAGS="-levent_openssl $LDFLAGS"
+      AC_DEFINE(HAVE_EVENT_OPENSSL_LIB, 1, [ ])
+    ], [
+      AC_MSG_ERROR([bufferevent_openssl_get_ssl not found in event_openssl library, or the library is not installed])
+    ])
 
     event_src="$event_src classes/ssl_context.c"
   fi

examples/ssl-echo-server/client.sh

+#!/bin/bash - 
+
+if [ $# -gt 0 ]; then
+	server_port=$1
+else
+	server_port=9999
+fi
+
+DIR="$( cd "$( dirname "$0" )" && pwd )"
+
+cert_file=$DIR"/client.pem"
+ca_file=$DIR"/server.crt"
+
+socat - SSL:127.0.0.1:$server_port,verify=1,cert=$cert_file,cafile=$ca_file

examples/ssl-echo-server/pem-client.sh

+#!/bin/bash - 
+
+cp -f server.pem client.pem
+cp -f server.crt client.crt 

examples/ssl-echo-server/pem-server.sh

+#!/bin/bash - 
+
+# generate a private key
+openssl genrsa -out server.key 1024
+
+# generate a self signed cert:
+openssl req -new -key server.key -x509 -days 3653 -out server.crt
+
+#     enter fields... (may all be empty when cert is only used privately)
+
+# generate the pem file:
+cat server.key server.crt > server.pem
+
+# secure permissions:
+chmod 600 server.key server.pem

examples/ssl-echo-server/server.php

+<?php
+/*
+ * SSL echo server
+ *
+ * To test it:
+ * 1) Generate certificates:
+ *
+ *	$ ./pem-server.sh
+ *	# Fill fields ...
+ *	$ ./pem-client.sh
+ *
+ * 2) Run the server:
+ *	$ php ./server.php
+ *	Optionally provide port:
+ *	$ php ./server.php 9999
+ *
+ * 3) In another terminal window run client:
+ *
+ *	$ ./client.sh
+ *	Optionally provide port:
+ *	$ ./client.sh 9999
+ */
+
+// This callback is invoked when there is data to read on $bev.
+function ssl_read_cb($bev, $ctx) {
+	$in = $bev->getInput();
+
+	printf("Received %zu bytes\n", $in->length);
+    printf("----- data ----\n");
+    printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));
+
+	$bev->writeBuffer($in);
+}
+
+// This callback is invoked when some even occurs on the event listener,
+// e.g. connection closed, or an error occured
+function ssl_event_cb($bev, $events, $ctx) {
+	if ($events & EventBufferEvent::ERROR) {
+		// Fetch errors from the SSL error stack
+		while ($err = $bev->sslError()) {
+			fprintf(STDERR, "Bufferevent error %s.\n", $err);
+		}
+	}
+
+	if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
+		$bev->free();
+	}
+}
+
+// This callback is invoked when a client accepts new connection
+function ssl_accept_cb($listener, $fd, $address, $ctx) {
+	// We got a new connection! Set up a bufferevent for it.
+	$base = $listener->getBase();
+
+	$bev = EventBufferEvent::sslSocket($base, $fd, $ctx,
+		EventBufferEvent::SSL_ACCEPTING, EventBufferEvent::OPT_CLOSE_ON_FREE);
+
+	if (!$bev) {
+		echo "Failed creating ssl buffer\n";
+		$base->exit(NULL);
+		exit(1);
+	}
+
+	$bev->enable(Event::READ);
+	$bev->setCallbacks("ssl_read_cb", NULL, "ssl_event_cb", NULL);
+}
+
+// This callback is invoked when we failed to setup new connection for a client
+function accept_error_cb($listener, $ctx) {
+	$base = $listener->getBase();
+
+	fprintf(STDERR, "Got an error %d (%s) on the listener. "
+		."Shutting down.\n",
+		EventUtil::getLastSocketErrno(),
+		EventUtil::getLastSocketError());
+
+	$base->exit(NULL);
+}
+
+// Initialize SSL structures, create an EventSslContext
+// Optionally create self-signed certificates
+function init_ssl($port) {
+	// We *must* have entropy. Otherwise there's no point to crypto.
+	if (!EventUtil::sslRandPoll()) {
+		exit("EventUtil::sslRandPoll failed\n");
+	}
+
+	$pem_passphrase = "echo server";
+	$pem_file       = __DIR__. "/server.pem";
+	$ca_file        = __DIR__. "/client.crt";
+
+	$pem_dn = array (
+ 		"countryName"            => "RU",
+ 		"stateOrProvinceName"    => "Moscow",
+ 		"localityName"           => "Moscow",
+ 		"organizationName"       => "Fancy Company",
+ 		"organizationalUnitName" => "Fancy Department",
+ 		"commonName"             => "devbox",
+ 		"emailAddress"           => "rrosmanov@gmail.com"
+	);
+	if (!file_exists($pem_file)) {
+		system("./pem-server.sh; ./pem-client.sh");
+	}
+
+	$ctx = new EventSslContext(EventSslContext::SSLv3_SERVER_METHOD, array (
+ 		EventSslContext::OPT_LOCAL_CERT        => $pem_file,
+ 		EventSslContext::OPT_CA_FILE           => $ca_file,
+ 		EventSslContext::OPT_PASSPHRASE        => $pem_passphrase,
+ 		EventSslContext::OPT_ALLOW_SELF_SIGNED => true,
+ 		EventSslContext::OPT_VERIFY_PEER       => true,
+	));
+	return $ctx;
+}
+
+// Allow to override the port
+$port = 9999;
+if ($argc > 1) {
+	$port = (int) $argv[1];
+}
+if ($port <= 0 || $port > 65535) {
+	exit("Invalid port\n");
+}
+
+$host = "127.0.0.1";
+
+$ctx = init_ssl($port);
+if (!$ctx) {
+	exit("Failed creating SSL context\n");
+}
+
+$base = new EventBase();
+if (!$base) {
+	exit("Couldn't open event base\n");
+}
+
+$listener = new EventListener($base, "ssl_accept_cb", $ctx,
+	EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
+	-1, "$host:$port");
+if (!$listener) {
+	exit("Couldn't create listener\n");
+}
+$listener->setErrorCallback("accept_error_cb");
+
+$base->dispatch();

examples/ssl_echo_server.php

-<?php
-//
-// To test: socat - SSL:devbox:9999,verify=0
-//
-
-// Creates self-signed certificate in PEM format
-function create_cert($pem_file, $pem_passphrase, $pem_dn) {
- 	$privkey = openssl_pkey_new();
- 	$cert    = openssl_csr_new($pem_dn, $privkey);
- 	$cert    = openssl_csr_sign($cert, null, $privkey, 365);
-
- 	$pem = array();
- 	openssl_x509_export($cert, $pem[0]);
- 	openssl_pkey_export($privkey, $pem[1], $pem_passphrase);
- 	$pem = implode($pem);
-
- 	file_put_contents($pem_file, $pem);
- 	chmod($pem_file, 0600);
-}
-
-// This callback is invoked when there is data to read on $bev.
-function ssl_read_cb($bev, $ctx) {
-	$in = $bev->getInput();
-
-	printf("Received %zu bytes\n", $in->length);
-    printf("----- data ----\n");
-    printf("%ld:\t%s\n", (int) $in->length, $in->pullup(-1));
-
-	$bev->writeBuffer($in);
-}
-
-function ssl_event_cb($bev, $events, $ctx) {
-	if ($events & EventBufferEvent::ERROR) {
-		while ($err = $bev->sslError()) {
-			fprintf(STDERR, "Bufferevent error %s.\n", $err);
-		}
-	}
-
-	if ($events & (EventBufferEvent::EOF | EventBufferEvent::ERROR)) {
-		$bev->free();
-	}
-}
-
-function ssl_accept_cb($listener, $fd, $address, $ctx) {
-	// We got a new connection! Set up a bufferevent for it.
-	$base = $listener->getBase();
-
-	$bev = EventBufferEvent::sslSocket($base, $fd, $ctx,
-		EventBufferEvent::SSL_ACCEPTING, EventBufferEvent::OPT_CLOSE_ON_FREE);
-
-	if (!$bev) {
-		echo "Failed creating ssl buffer\n";
-		$base->exit(NULL);
-		exit(1);
-	}
-
-	$bev->enable(Event::READ);
-	$bev->setCallbacks("ssl_read_cb", NULL, "ssl_event_cb", NULL);
-}
-
-function accept_error_cb($listener, $ctx) {
-	$base = $listener->getBase();
-
-	fprintf(STDERR, "Got an error %d (%s) on the listener. "
-		."Shutting down.\n",
-		EventUtil::getLastSocketErrno(),
-		EventUtil::getLastSocketError());
-
-	$base->exit(NULL);
-}
-
-function init_ssl() {
-	// We *must* have entropy. Otherwise there's no point to crypto.
-	if (!EventUtil::sslRandPoll()) {
-		exit("EventUtil::sslRandPoll failed\n");
-	}
-
-	$pem_passphrase = "echo server";
-	$pem_file       = "cert.pem";
-
-	$pem_dn = array (
- 		"countryName"            => "RU",
- 		"stateOrProvinceName"    => "Moscow",
- 		"localityName"           => "Moscow",
- 		"organizationName"       => "Fancy Company",
- 		"organizationalUnitName" => "Fancy Department",
- 		"commonName"             => "devbox",
- 		"emailAddress"           => "rrosmanov@gmail.com"
-	);
-	create_cert($pem_file, $pem_passphrase, $pem_dn);
-
-	$ctx = new EventSslContext(EventSslContext::SSLv23_SERVER_METHOD, array (
- 		EventSslContext::OPT_LOCAL_CERT        => $pem_file,
- 		EventSslContext::OPT_PASSPHRASE        => $pem_passphrase,
- 		EventSslContext::OPT_ALLOW_SELF_SIGNED => true,
- 		EventSslContext::OPT_VERIFY_PEER       => false,
-	));
-	return $ctx;
-}
-
-$port = 9999;
-if ($argc > 1) {
-	$port = (int) $argv[1];
-}
-if ($port <= 0 || $port > 65535) {
-	exit("Invalid port\n");
-}
-
-$host = "127.0.0.1";
-
-$ctx = init_ssl();
-if (!$ctx) {
-	exit("Failed creating SSL context\n");
-}
-
-$base = new EventBase();
-if (!$base) {
-	exit("Couldn't open event base\n");
-}
-
-$listener = new EventListener($base, "ssl_accept_cb", $ctx,
-	EventListener::OPT_CLOSE_ON_FREE | EventListener::OPT_REUSEABLE,
-	-1, "$host:$port");
-if (!$listener) {
-	exit("Couldn't create listener\n");
-}
-$listener->setErrorCallback("accept_error_cb");
-
-$base->dispatch();
     <configureoption default="/usr" name="with-event-libevent-dir" prompt="libevent installation prefix"/>
     <configureoption default="yes" name="with-event-extra" prompt="Include libevent protocol-specific functionality support including HTTP, DNS, and RPC"/>
     <configureoption default="yes" name="with-event-openssl" prompt="Include libevent OpenSSL support"/>
-    <configureoption default="yes" name="with-openssl-dir" prompt="openssl installation prefix"/>
+    <configureoption default="no" name="with-openssl-dir" prompt="openssl installation prefix"/>
   </extsrcrelease>
   <!--{{{ changelog-->
   <changelog>
 static HashTable event_buffer_pos_properties;
 #ifdef HAVE_EVENT_OPENSSL_LIB
 static HashTable event_ssl_context_properties;
+int php_event_ssl_data_index;
 #endif
 
+
 static zend_object_handlers object_handlers;
 
 static const zend_module_dep event_deps[] = {
 	ZEND_MOD_OPTIONAL("sockets")
-	/*ZEND_MOD_OPTIONAL("openssl")*/
 	{NULL, NULL, NULL}
 };
 
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_ssl_context_ce, OPT_VERIFY_PEER,       PHP_EVENT_OPT_VERIFY_PEER);
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_ssl_context_ce, OPT_VERIFY_DEPTH,      PHP_EVENT_OPT_VERIFY_DEPTH);
 	REGISTER_EVENT_CLASS_CONST_LONG(php_event_ssl_context_ce, OPT_CIPHERS,           PHP_EVENT_OPT_CIPHERS);
-#endif
+
+	/* Initialize openssl library */
+    SSL_library_init();
+    OpenSSL_add_all_ciphers();
+    OpenSSL_add_all_digests();
+    OpenSSL_add_all_algorithms();
+    SSL_load_error_strings();
+
+	/* Create new index which will be used to retreive custom data of the OpenSSL callbacks */
+    php_event_ssl_data_index = SSL_get_ex_new_index(0, "PHP EventSslContext index", NULL, NULL, NULL);
+#endif /* HAVE_EVENT_OPENSSL_LIB */
 
 	/* Handle libevent's error logging more gracefully than it's default
 	 * logging to stderr, or calling abort()/exit() */
 /* {{{ PHP_MSHUTDOWN_FUNCTION */
 PHP_MSHUTDOWN_FUNCTION(event)
 {
+#ifdef HAVE_EVENT_OPENSSL_LIB
+	/* Removes memory allocated when loading digest and cipher names
+	 * in the OpenSSL_add_all_ family of functions */
+	EVP_cleanup();
+#endif
+
 #if LIBEVENT_VERSION_NUMBER >= 0x02010000
 	/* libevent_global_shutdown is available since libevent 2.1.0-alpha.
 	 *
 #ifndef PHP_EVENT_PRIV_H
 #define PHP_EVENT_PRIV_H
 
+#ifdef HAVE_EVENT_OPENSSL_LIB
+extern int php_event_ssl_data_index;
+#endif
+
 extern const zend_function_entry php_event_ce_functions[];
 extern const zend_function_entry php_event_base_ce_functions[];
 extern const zend_function_entry php_event_config_ce_functions[];
 extern zend_class_entry *php_event_http_ce;
 #endif /* HAVE_EVENT_EXTRA_LIB */
 
-
-
 #endif /* PHP_EVENT_PRIV_H */
 
 /* 
 typedef struct _php_event_ssl_context_t {
 	PHP_EVENT_OBJECT_HEAD;
 
-	SSL_CTX *ctx;
+	SSL_CTX   *ctx;
 	HashTable *ht;
 } php_event_ssl_context_t;
 #endif
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.