1 /**
2  * Copyright 2015-2017 DataStax, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "php_driver.h"
18 #include "php_driver_types.h"
19 
20 #include <ext/standard/php_filestat.h>
21 
22 zend_class_entry *php_driver_ssl_builder_ce = NULL;
23 
24 static int
file_get_contents(char * path,char ** output,int * len TSRMLS_DC)25 file_get_contents(char *path, char **output, int *len TSRMLS_DC)
26 {
27 #if PHP_MAJOR_VERSION >= 7
28   zend_string *str;
29   php_stream *stream = php_stream_open_wrapper(path, "rb",
30                          USE_PATH|REPORT_ERRORS, NULL);
31 #else
32   php_stream *stream = php_stream_open_wrapper(path, "rb",
33                          USE_PATH|REPORT_ERRORS|ENFORCE_SAFE_MODE, NULL);
34 #endif
35 
36   if (!stream) {
37     zend_throw_exception_ex(php_driver_runtime_exception_ce, 0 TSRMLS_CC,
38       "The path '%s' doesn't exist or is not readable", path);
39     return 0;
40   }
41 
42 #if PHP_MAJOR_VERSION >= 7
43   str = php_stream_copy_to_mem(stream, PHP_STREAM_COPY_ALL, 0);
44   if (str) {
45     *output = estrndup(str->val, str->len);
46     *len = str->len;
47     zend_string_release(str);
48   } else {
49     php_stream_close(stream);
50     return 0;
51   }
52 #else
53   *len = php_stream_copy_to_mem(stream, output, PHP_STREAM_COPY_ALL, 0);
54 #endif
55 
56   php_stream_close(stream);
57   return 1;
58 }
59 
PHP_METHOD(SSLOptionsBuilder,build)60 PHP_METHOD(SSLOptionsBuilder, build)
61 {
62   php_driver_ssl *ssl = NULL;
63   int   len;
64   char *contents;
65   CassError rc;
66 
67   php_driver_ssl_builder *builder = PHP_DRIVER_GET_SSL_BUILDER(getThis());
68 
69   object_init_ex(return_value, php_driver_ssl_ce);
70   ssl = PHP_DRIVER_GET_SSL(return_value);
71 
72   cass_ssl_set_verify_flags(ssl->ssl, builder->flags);
73 
74   if (builder->trusted_certs) {
75     int   i;
76     char *path;
77 
78     for (i = 0; i < builder->trusted_certs_cnt; i++) {
79       path = builder->trusted_certs[i];
80 
81       if (!file_get_contents(path, &contents, &len TSRMLS_CC))
82         return;
83 
84       rc = cass_ssl_add_trusted_cert_n(ssl->ssl, contents, len);
85       efree(contents);
86       ASSERT_SUCCESS(rc);
87     }
88   }
89 
90   if (builder->client_cert) {
91     if (!file_get_contents(builder->client_cert, &contents, &len TSRMLS_CC))
92       return;
93 
94     rc = cass_ssl_set_cert_n(ssl->ssl, contents, len);
95     efree(contents);
96     ASSERT_SUCCESS(rc);
97   }
98 
99   if (builder->private_key) {
100     if (!file_get_contents(builder->private_key, &contents, &len TSRMLS_CC))
101       return;
102 
103     rc = cass_ssl_set_private_key(ssl->ssl, contents, builder->passphrase);
104     efree(contents);
105     ASSERT_SUCCESS(rc);
106   }
107 }
108 
PHP_METHOD(SSLOptionsBuilder,withTrustedCerts)109 PHP_METHOD(SSLOptionsBuilder, withTrustedCerts)
110 {
111   zval readable;
112   php5to7_zval_args args = NULL;
113   int argc = 0, i;
114   php_driver_ssl_builder *builder = NULL;
115 
116   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "+", &args, &argc) == FAILURE) {
117     return;
118   }
119 
120   for (i = 0; i < argc; i++) {
121     zval *path = PHP5TO7_ZVAL_ARG(args[i]);
122 
123     if (Z_TYPE_P(path) != IS_STRING) {
124       throw_invalid_argument(path, "path", "a path to a trusted cert file" TSRMLS_CC);
125       PHP5TO7_MAYBE_EFREE(args);
126     }
127 
128     php_stat(Z_STRVAL_P(path), Z_STRLEN_P(path), FS_IS_R, &readable TSRMLS_CC);
129 
130     if (PHP5TO7_ZVAL_IS_FALSE_P(&readable)) {
131       zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
132         "The path '%s' doesn't exist or is not readable", Z_STRVAL_P(path));
133       PHP5TO7_MAYBE_EFREE(args);
134       return;
135     }
136   }
137 
138   builder = PHP_DRIVER_GET_SSL_BUILDER(getThis());
139 
140   if (builder->trusted_certs) {
141     for (i = 0; i < builder->trusted_certs_cnt; i++) {
142       efree(builder->trusted_certs[i]);
143     }
144 
145     efree(builder->trusted_certs);
146   }
147 
148   builder->trusted_certs_cnt = argc;
149   builder->trusted_certs     = ecalloc(argc, sizeof(char *));
150 
151   for (i = 0; i < argc; i++) {
152     zval *path = PHP5TO7_ZVAL_ARG(args[i]);
153 
154     builder->trusted_certs[i] = estrndup(Z_STRVAL_P(path), Z_STRLEN_P(path));
155   }
156 
157   PHP5TO7_MAYBE_EFREE(args);
158   RETURN_ZVAL(getThis(), 1, 0);
159 }
160 
PHP_METHOD(SSLOptionsBuilder,withVerifyFlags)161 PHP_METHOD(SSLOptionsBuilder, withVerifyFlags)
162 {
163   long flags;
164   php_driver_ssl_builder *builder = NULL;
165 
166   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "l", &flags) == FAILURE) {
167     return;
168   }
169 
170   builder = PHP_DRIVER_GET_SSL_BUILDER(getThis());
171 
172   builder->flags = (int) flags;
173 
174   RETURN_ZVAL(getThis(), 1, 0);
175 }
176 
PHP_METHOD(SSLOptionsBuilder,withClientCert)177 PHP_METHOD(SSLOptionsBuilder, withClientCert)
178 {
179   char *client_cert;
180   php5to7_size client_cert_len;
181   zval readable;
182   php_driver_ssl_builder *builder = NULL;
183 
184   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &client_cert, &client_cert_len) == FAILURE) {
185     return;
186   }
187 
188   php_stat(client_cert, client_cert_len, FS_IS_R, &readable TSRMLS_CC);
189 
190   if (PHP5TO7_ZVAL_IS_FALSE_P(&readable)) {
191     zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
192       "The path '%s' doesn't exist or is not readable", client_cert);
193     return;
194   }
195 
196   builder = PHP_DRIVER_GET_SSL_BUILDER(getThis());
197 
198   if (builder->client_cert)
199     efree(builder->client_cert);
200 
201   builder->client_cert = estrndup(client_cert, client_cert_len);
202 
203   RETURN_ZVAL(getThis(), 1, 0);
204 }
205 
PHP_METHOD(SSLOptionsBuilder,withPrivateKey)206 PHP_METHOD(SSLOptionsBuilder, withPrivateKey)
207 {
208   char *private_key;
209   char *passphrase = NULL;
210   php5to7_size private_key_len, passphrase_len;
211   zval readable;
212   php_driver_ssl_builder *builder = NULL;
213 
214   if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|s", &private_key, &private_key_len, &passphrase, &passphrase_len) == FAILURE) {
215     return;
216   }
217 
218   php_stat(private_key, private_key_len, FS_IS_R, &readable TSRMLS_CC);
219 
220   if (PHP5TO7_ZVAL_IS_FALSE_P(&readable)) {
221     zend_throw_exception_ex(php_driver_invalid_argument_exception_ce, 0 TSRMLS_CC,
222       "The path '%s' doesn't exist or is not readable", private_key);
223     return;
224   }
225 
226   builder = PHP_DRIVER_GET_SSL_BUILDER(getThis());
227 
228   if (builder->private_key)
229     efree(builder->private_key);
230 
231   builder->private_key = estrndup(private_key, private_key_len);
232 
233   if (builder->passphrase) {
234     efree(builder->passphrase);
235     builder->passphrase = NULL;
236   }
237 
238   if (passphrase)
239     builder->passphrase = estrndup(passphrase, passphrase_len);
240 
241   RETURN_ZVAL(getThis(), 1, 0);
242 }
243 
244 ZEND_BEGIN_ARG_INFO_EX(arginfo_none, 0, ZEND_RETURN_VALUE, 0)
245 ZEND_END_ARG_INFO()
246 
247 ZEND_BEGIN_ARG_INFO_EX(arginfo_path, 0, ZEND_RETURN_VALUE, 1)
248   ZEND_ARG_INFO(0, path)
249 ZEND_END_ARG_INFO()
250 
251 ZEND_BEGIN_ARG_INFO_EX(arginfo_flags, 0, ZEND_RETURN_VALUE, 1)
252   ZEND_ARG_INFO(0, flags)
253 ZEND_END_ARG_INFO()
254 
255 ZEND_BEGIN_ARG_INFO_EX(arginfo_key, 0, ZEND_RETURN_VALUE, 1)
256   ZEND_ARG_INFO(0, path)
257   ZEND_ARG_INFO(0, passphrase)
258 ZEND_END_ARG_INFO()
259 
260 static zend_function_entry php_driver_ssl_builder_methods[] = {
261   PHP_ME(SSLOptionsBuilder, build, arginfo_none, ZEND_ACC_PUBLIC)
262   PHP_ME(SSLOptionsBuilder, withTrustedCerts, arginfo_path,
263     ZEND_ACC_PUBLIC)
264   PHP_ME(SSLOptionsBuilder, withVerifyFlags, arginfo_flags,
265     ZEND_ACC_PUBLIC)
266   PHP_ME(SSLOptionsBuilder, withClientCert, arginfo_path,
267     ZEND_ACC_PUBLIC)
268   PHP_ME(SSLOptionsBuilder, withPrivateKey, arginfo_key,
269     ZEND_ACC_PUBLIC)
270   PHP_FE_END
271 };
272 
273 static zend_object_handlers php_driver_ssl_builder_handlers;
274 
275 static HashTable *
php_driver_ssl_builder_properties(zval * object TSRMLS_DC)276 php_driver_ssl_builder_properties(zval *object TSRMLS_DC)
277 {
278   HashTable *props = zend_std_get_properties(object TSRMLS_CC);
279 
280   return props;
281 }
282 
283 static int
php_driver_ssl_builder_compare(zval * obj1,zval * obj2 TSRMLS_DC)284 php_driver_ssl_builder_compare(zval *obj1, zval *obj2 TSRMLS_DC)
285 {
286   if (Z_OBJCE_P(obj1) != Z_OBJCE_P(obj2))
287     return 1; /* different classes */
288 
289   return Z_OBJ_HANDLE_P(obj1) != Z_OBJ_HANDLE_P(obj1);
290 }
291 
292 static void
php_driver_ssl_builder_free(php5to7_zend_object_free * object TSRMLS_DC)293 php_driver_ssl_builder_free(php5to7_zend_object_free *object TSRMLS_DC)
294 {
295   php_driver_ssl_builder *self = PHP5TO7_ZEND_OBJECT_GET(ssl_builder, object);
296 
297   if (self->trusted_certs) {
298     int i;
299 
300     for (i = 0; i < self->trusted_certs_cnt; i++)
301       efree(self->trusted_certs[i]);
302 
303     efree(self->trusted_certs);
304   }
305 
306   if (self->client_cert)
307     efree(self->client_cert);
308 
309   if (self->private_key)
310     efree(self->private_key);
311 
312   if (self->passphrase)
313     efree(self->passphrase);
314 
315   zend_object_std_dtor(&self->zval TSRMLS_CC);
316   PHP5TO7_MAYBE_EFREE(self);
317 }
318 
319 static php5to7_zend_object
php_driver_ssl_builder_new(zend_class_entry * ce TSRMLS_DC)320 php_driver_ssl_builder_new(zend_class_entry *ce TSRMLS_DC)
321 {
322   php_driver_ssl_builder *self =
323       PHP5TO7_ZEND_OBJECT_ECALLOC(ssl_builder, ce);
324 
325   self->flags             = 0;
326   self->trusted_certs     = NULL;
327   self->trusted_certs_cnt = 0;
328   self->client_cert       = NULL;
329   self->private_key       = NULL;
330   self->passphrase        = NULL;
331 
332   PHP5TO7_ZEND_OBJECT_INIT(ssl_builder, self, ce);
333 }
334 
php_driver_define_SSLOptionsBuilder(TSRMLS_D)335 void php_driver_define_SSLOptionsBuilder(TSRMLS_D)
336 {
337   zend_class_entry ce;
338 
339   INIT_CLASS_ENTRY(ce, PHP_DRIVER_NAMESPACE "\\SSLOptions\\Builder", php_driver_ssl_builder_methods);
340   php_driver_ssl_builder_ce = zend_register_internal_class(&ce TSRMLS_CC);
341   php_driver_ssl_builder_ce->ce_flags     |= PHP5TO7_ZEND_ACC_FINAL;
342   php_driver_ssl_builder_ce->create_object = php_driver_ssl_builder_new;
343 
344   memcpy(&php_driver_ssl_builder_handlers, zend_get_std_object_handlers(), sizeof(zend_object_handlers));
345   php_driver_ssl_builder_handlers.get_properties  = php_driver_ssl_builder_properties;
346   php_driver_ssl_builder_handlers.compare_objects = php_driver_ssl_builder_compare;
347 }
348