1 /*
2 *
3 * Copyright 2015 gRPC authors.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 /**
20 * class CallCredentials
21 * @see https://github.com/grpc/grpc/tree/master/src/php/ext/grpc/call_credentials.c
22 */
23
24 #include "call_credentials.h"
25
26 #include <ext/spl/spl_exceptions.h>
27 #include <zend_exceptions.h>
28
29 #include <grpc/support/log.h>
30 #include <grpc/support/string_util.h>
31
32 #include "call.h"
33
34 zend_class_entry *grpc_ce_call_credentials;
35 PHP_GRPC_DECLARE_OBJECT_HANDLER(call_credentials_ce_handlers)
36
37 /* Frees and destroys an instance of wrapped_grpc_call_credentials */
38 PHP_GRPC_FREE_WRAPPED_FUNC_START(wrapped_grpc_call_credentials)
39 if (p->wrapped != NULL) {
40 grpc_call_credentials_release(p->wrapped);
41 }
PHP_GRPC_FREE_WRAPPED_FUNC_END()42 PHP_GRPC_FREE_WRAPPED_FUNC_END()
43
44 /* Initializes an instance of wrapped_grpc_call_credentials to be
45 * associated with an object of a class specified by class_type */
46 php_grpc_zend_object create_wrapped_grpc_call_credentials(
47 zend_class_entry *class_type TSRMLS_DC) {
48 PHP_GRPC_ALLOC_CLASS_OBJECT(wrapped_grpc_call_credentials);
49 zend_object_std_init(&intern->std, class_type TSRMLS_CC);
50 object_properties_init(&intern->std, class_type);
51 PHP_GRPC_FREE_CLASS_OBJECT(wrapped_grpc_call_credentials,
52 call_credentials_ce_handlers);
53 }
54
grpc_php_wrap_call_credentials(grpc_call_credentials * wrapped TSRMLS_DC)55 zval *grpc_php_wrap_call_credentials(grpc_call_credentials
56 *wrapped TSRMLS_DC) {
57 zval *credentials_object;
58 PHP_GRPC_MAKE_STD_ZVAL(credentials_object);
59 object_init_ex(credentials_object, grpc_ce_call_credentials);
60 wrapped_grpc_call_credentials *credentials =
61 PHP_GRPC_GET_WRAPPED_OBJECT(wrapped_grpc_call_credentials,
62 credentials_object);
63 credentials->wrapped = wrapped;
64 return credentials_object;
65 }
66
67 /**
68 * Create composite credentials from two existing credentials.
69 * @param CallCredentials $cred1_obj The first credential
70 * @param CallCredentials $cred2_obj The second credential
71 * @return CallCredentials The new composite credentials object
72 */
PHP_METHOD(CallCredentials,createComposite)73 PHP_METHOD(CallCredentials, createComposite) {
74 zval *cred1_obj;
75 zval *cred2_obj;
76
77 /* "OO" == 2 Objects */
78 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "OO", &cred1_obj,
79 grpc_ce_call_credentials, &cred2_obj,
80 grpc_ce_call_credentials) == FAILURE) {
81 zend_throw_exception(spl_ce_InvalidArgumentException,
82 "createComposite expects 2 CallCredentials",
83 1 TSRMLS_CC);
84 return;
85 }
86 wrapped_grpc_call_credentials *cred1 =
87 PHP_GRPC_GET_WRAPPED_OBJECT(wrapped_grpc_call_credentials, cred1_obj);
88 wrapped_grpc_call_credentials *cred2 =
89 PHP_GRPC_GET_WRAPPED_OBJECT(wrapped_grpc_call_credentials, cred2_obj);
90 grpc_call_credentials *creds =
91 grpc_composite_call_credentials_create(cred1->wrapped, cred2->wrapped,
92 NULL);
93 zval *creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC);
94 RETURN_DESTROY_ZVAL(creds_object);
95 }
96
97 /**
98 * Create a call credentials object from the plugin API
99 * @param function $fci The callback function
100 * @return CallCredentials The new call credentials object
101 */
PHP_METHOD(CallCredentials,createFromPlugin)102 PHP_METHOD(CallCredentials, createFromPlugin) {
103 zend_fcall_info *fci;
104 zend_fcall_info_cache *fci_cache;
105
106 fci = (zend_fcall_info *)malloc(sizeof(zend_fcall_info));
107 fci_cache = (zend_fcall_info_cache *)malloc(sizeof(zend_fcall_info_cache));
108 memset(fci, 0, sizeof(zend_fcall_info));
109 memset(fci_cache, 0, sizeof(zend_fcall_info_cache));
110
111 /* "f" == 1 function */
112 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "f*", fci, fci_cache,
113 fci->params, fci->param_count) == FAILURE) {
114 zend_throw_exception(spl_ce_InvalidArgumentException,
115 "createFromPlugin expects 1 callback", 1 TSRMLS_CC);
116 free(fci);
117 free(fci_cache);
118 return;
119 }
120
121 plugin_state *state;
122 state = (plugin_state *)malloc(sizeof(plugin_state));
123 memset(state, 0, sizeof(plugin_state));
124
125 /* save the user provided PHP callback function */
126 state->fci = fci;
127 state->fci_cache = fci_cache;
128
129 grpc_metadata_credentials_plugin plugin;
130 plugin.get_metadata = plugin_get_metadata;
131 plugin.destroy = plugin_destroy_state;
132 plugin.state = (void *)state;
133 plugin.type = "";
134 // TODO(yihuazhang): Expose min_security_level via the PHP API so that
135 // applications can decide what minimum security level their plugins require.
136 grpc_call_credentials *creds =
137 grpc_metadata_credentials_create_from_plugin(plugin, GRPC_PRIVACY_AND_INTEGRITY, NULL);
138 zval *creds_object = grpc_php_wrap_call_credentials(creds TSRMLS_CC);
139 RETURN_DESTROY_ZVAL(creds_object);
140 }
141
142 /* Callback function for plugin creds API */
plugin_get_metadata(void * ptr,grpc_auth_metadata_context context,grpc_credentials_plugin_metadata_cb cb,void * user_data,grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],size_t * num_creds_md,grpc_status_code * status,const char ** error_details)143 int plugin_get_metadata(
144 void *ptr, grpc_auth_metadata_context context,
145 grpc_credentials_plugin_metadata_cb cb, void *user_data,
146 grpc_metadata creds_md[GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX],
147 size_t *num_creds_md, grpc_status_code *status,
148 const char **error_details) {
149 TSRMLS_FETCH();
150
151 plugin_state *state = (plugin_state *)ptr;
152
153 /* prepare to call the user callback function with info from the
154 * grpc_auth_metadata_context */
155 zval *arg;
156 PHP_GRPC_MAKE_STD_ZVAL(arg);
157 object_init(arg);
158 php_grpc_add_property_string(arg, "service_url", context.service_url, true);
159 php_grpc_add_property_string(arg, "method_name", context.method_name, true);
160 zval *retval = NULL;
161 #if PHP_MAJOR_VERSION < 7
162 zval **params[1];
163 params[0] = &arg;
164 state->fci->params = params;
165 state->fci->retval_ptr_ptr = &retval;
166 #else
167 PHP_GRPC_MAKE_STD_ZVAL(retval);
168 state->fci->params = arg;
169 state->fci->retval = retval;
170 #endif
171 state->fci->param_count = 1;
172
173 PHP_GRPC_DELREF(arg);
174
175 gpr_log(GPR_INFO, "GRPC_PHP: call credentials plugin function - begin");
176 /* call the user callback function */
177 zend_call_function(state->fci, state->fci_cache TSRMLS_CC);
178 gpr_log(GPR_INFO, "GRPC_PHP: call credentials plugin function - end");
179
180 *num_creds_md = 0;
181 *status = GRPC_STATUS_OK;
182 *error_details = NULL;
183
184 bool should_return = false;
185 grpc_metadata_array metadata;
186
187 if (retval == NULL || Z_TYPE_P(retval) != IS_ARRAY) {
188 *status = GRPC_STATUS_INVALID_ARGUMENT;
189 should_return = true; // Synchronous return.
190 }
191 if (!create_metadata_array(retval, &metadata)) {
192 *status = GRPC_STATUS_INVALID_ARGUMENT;
193 should_return = true; // Synchronous return.
194 grpc_php_metadata_array_destroy_including_entries(&metadata);
195 }
196
197 if (retval != NULL) {
198 #if PHP_MAJOR_VERSION < 7
199 zval_ptr_dtor(&retval);
200 #else
201 zval_ptr_dtor(arg);
202 zval_ptr_dtor(retval);
203 PHP_GRPC_FREE_STD_ZVAL(arg);
204 PHP_GRPC_FREE_STD_ZVAL(retval);
205 #endif
206 }
207 if (should_return) {
208 return true;
209 }
210
211 if (metadata.count > GRPC_METADATA_CREDENTIALS_PLUGIN_SYNC_MAX) {
212 *status = GRPC_STATUS_INTERNAL;
213 *error_details = gpr_strdup(
214 "PHP plugin credentials returned too many metadata entries");
215 for (size_t i = 0; i < metadata.count; i++) {
216 // TODO(stanleycheung): Why don't we need to unref the key here?
217 grpc_slice_unref(metadata.metadata[i].value);
218 }
219 } else {
220 // Return data to core.
221 *num_creds_md = metadata.count;
222 for (size_t i = 0; i < metadata.count; ++i) {
223 creds_md[i] = metadata.metadata[i];
224 }
225 }
226
227 grpc_metadata_array_destroy(&metadata);
228 return true; // Synchronous return.
229 }
230
231 /* Cleanup function for plugin creds API */
plugin_destroy_state(void * ptr)232 void plugin_destroy_state(void *ptr) {
233 plugin_state *state = (plugin_state *)ptr;
234 free(state->fci);
235 free(state->fci_cache);
236 #if PHP_MAJOR_VERSION < 7
237 PHP_GRPC_FREE_STD_ZVAL(state->fci->params);
238 PHP_GRPC_FREE_STD_ZVAL(state->fci->retval);
239 #endif
240 free(state);
241 }
242
243 ZEND_BEGIN_ARG_INFO_EX(arginfo_createComposite, 0, 0, 2)
244 ZEND_ARG_INFO(0, creds1)
245 ZEND_ARG_INFO(0, creds2)
246 ZEND_END_ARG_INFO()
247
248 ZEND_BEGIN_ARG_INFO_EX(arginfo_createFromPlugin, 0, 0, 1)
249 ZEND_ARG_INFO(0, callback)
250 ZEND_END_ARG_INFO()
251
252 static zend_function_entry call_credentials_methods[] = {
253 PHP_ME(CallCredentials, createComposite, arginfo_createComposite,
254 ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
255 PHP_ME(CallCredentials, createFromPlugin, arginfo_createFromPlugin,
256 ZEND_ACC_PUBLIC | ZEND_ACC_STATIC)
257 PHP_FE_END
258 };
259
grpc_init_call_credentials(TSRMLS_D)260 void grpc_init_call_credentials(TSRMLS_D) {
261 zend_class_entry ce;
262 INIT_CLASS_ENTRY(ce, "Grpc\\CallCredentials", call_credentials_methods);
263 ce.create_object = create_wrapped_grpc_call_credentials;
264 grpc_ce_call_credentials = zend_register_internal_class(&ce TSRMLS_CC);
265 PHP_GRPC_INIT_HANDLER(wrapped_grpc_call_credentials,
266 call_credentials_ce_handlers);
267 }
268