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 #include <ruby/ruby.h>
20
21 #include "rb_channel_credentials.h"
22
23 #include <string.h>
24
25 #include "rb_call_credentials.h"
26 #include "rb_grpc.h"
27 #include "rb_grpc_imports.generated.h"
28
29 #include <grpc/grpc.h>
30 #include <grpc/grpc_security.h>
31 #include <grpc/support/alloc.h>
32 #include <grpc/support/log.h>
33
34 /* grpc_rb_cChannelCredentials is the ruby class that proxies
35 grpc_channel_credentials. */
36 static VALUE grpc_rb_cChannelCredentials = Qnil;
37
38 static char* pem_root_certs = NULL;
39
40 /* grpc_rb_channel_credentials wraps a grpc_channel_credentials. It provides a
41 * mark object that is used to hold references to any objects used to create
42 * the credentials. */
43 typedef struct grpc_rb_channel_credentials {
44 /* Holder of ruby objects involved in constructing the credentials */
45 VALUE mark;
46
47 /* The actual credentials */
48 grpc_channel_credentials* wrapped;
49 } grpc_rb_channel_credentials;
50
grpc_rb_channel_credentials_free_internal(void * p)51 static void grpc_rb_channel_credentials_free_internal(void* p) {
52 grpc_rb_channel_credentials* wrapper = NULL;
53 if (p == NULL) {
54 return;
55 };
56 wrapper = (grpc_rb_channel_credentials*)p;
57 grpc_channel_credentials_release(wrapper->wrapped);
58 wrapper->wrapped = NULL;
59
60 xfree(p);
61 }
62
63 /* Destroys the credentials instances. */
grpc_rb_channel_credentials_free(void * p)64 static void grpc_rb_channel_credentials_free(void* p) {
65 grpc_rb_channel_credentials_free_internal(p);
66 }
67
68 /* Protects the mark object from GC */
grpc_rb_channel_credentials_mark(void * p)69 static void grpc_rb_channel_credentials_mark(void* p) {
70 grpc_rb_channel_credentials* wrapper = NULL;
71 if (p == NULL) {
72 return;
73 }
74 wrapper = (grpc_rb_channel_credentials*)p;
75
76 if (wrapper->mark != Qnil) {
77 rb_gc_mark(wrapper->mark);
78 }
79 }
80
81 static rb_data_type_t grpc_rb_channel_credentials_data_type = {
82 "grpc_channel_credentials",
83 {grpc_rb_channel_credentials_mark,
84 grpc_rb_channel_credentials_free,
85 GRPC_RB_MEMSIZE_UNAVAILABLE,
86 {NULL, NULL}},
87 NULL,
88 NULL,
89 #ifdef RUBY_TYPED_FREE_IMMEDIATELY
90 RUBY_TYPED_FREE_IMMEDIATELY
91 #endif
92 };
93
94 /* Allocates ChannelCredential instances.
95 Provides safe initial defaults for the instance fields. */
grpc_rb_channel_credentials_alloc(VALUE cls)96 static VALUE grpc_rb_channel_credentials_alloc(VALUE cls) {
97 grpc_ruby_init();
98 grpc_rb_channel_credentials* wrapper = ALLOC(grpc_rb_channel_credentials);
99 wrapper->wrapped = NULL;
100 wrapper->mark = Qnil;
101 return TypedData_Wrap_Struct(cls, &grpc_rb_channel_credentials_data_type,
102 wrapper);
103 }
104
105 /* Creates a wrapping object for a given channel credentials. This should only
106 * be called with grpc_channel_credentials objects that are not already
107 * associated with any Ruby object. */
grpc_rb_wrap_channel_credentials(grpc_channel_credentials * c,VALUE mark)108 VALUE grpc_rb_wrap_channel_credentials(grpc_channel_credentials* c,
109 VALUE mark) {
110 VALUE rb_wrapper;
111 grpc_rb_channel_credentials* wrapper;
112 if (c == NULL) {
113 return Qnil;
114 }
115 rb_wrapper = grpc_rb_channel_credentials_alloc(grpc_rb_cChannelCredentials);
116 TypedData_Get_Struct(rb_wrapper, grpc_rb_channel_credentials,
117 &grpc_rb_channel_credentials_data_type, wrapper);
118 wrapper->wrapped = c;
119 wrapper->mark = mark;
120 return rb_wrapper;
121 }
122
123 /* The attribute used on the mark object to hold the pem_root_certs. */
124 static ID id_pem_root_certs;
125
126 /* The attribute used on the mark object to hold the pem_private_key. */
127 static ID id_pem_private_key;
128
129 /* The attribute used on the mark object to hold the pem_private_key. */
130 static ID id_pem_cert_chain;
131
132 /*
133 call-seq:
134 creds1 = Credentials.new()
135 ...
136 creds2 = Credentials.new(pem_root_certs)
137 ...
138 creds3 = Credentials.new(pem_root_certs, pem_private_key,
139 pem_cert_chain)
140 pem_root_certs: (optional) PEM encoding of the server root certificate
141 pem_private_key: (optional) PEM encoding of the client's private key
142 pem_cert_chain: (optional) PEM encoding of the client's cert chain
143 Initializes Credential instances. */
grpc_rb_channel_credentials_init(int argc,VALUE * argv,VALUE self)144 static VALUE grpc_rb_channel_credentials_init(int argc, VALUE* argv,
145 VALUE self) {
146 VALUE pem_root_certs = Qnil;
147 VALUE pem_private_key = Qnil;
148 VALUE pem_cert_chain = Qnil;
149 grpc_rb_channel_credentials* wrapper = NULL;
150 grpc_channel_credentials* creds = NULL;
151 grpc_ssl_pem_key_cert_pair key_cert_pair;
152 const char* pem_root_certs_cstr = NULL;
153 MEMZERO(&key_cert_pair, grpc_ssl_pem_key_cert_pair, 1);
154
155 /* "03" == no mandatory arg, 3 optional */
156 rb_scan_args(argc, argv, "03", &pem_root_certs, &pem_private_key,
157 &pem_cert_chain);
158
159 TypedData_Get_Struct(self, grpc_rb_channel_credentials,
160 &grpc_rb_channel_credentials_data_type, wrapper);
161 if (pem_root_certs != Qnil) {
162 pem_root_certs_cstr = RSTRING_PTR(pem_root_certs);
163 }
164 if (pem_private_key == Qnil && pem_cert_chain == Qnil) {
165 creds = grpc_ssl_credentials_create(pem_root_certs_cstr, NULL, NULL, NULL);
166 } else {
167 if (pem_private_key == Qnil) {
168 rb_raise(
169 rb_eRuntimeError,
170 "could not create a credentials because pem_private_key is NULL");
171 }
172 if (pem_cert_chain == Qnil) {
173 rb_raise(rb_eRuntimeError,
174 "could not create a credentials because pem_cert_chain is NULL");
175 }
176 key_cert_pair.private_key = RSTRING_PTR(pem_private_key);
177 key_cert_pair.cert_chain = RSTRING_PTR(pem_cert_chain);
178 creds = grpc_ssl_credentials_create(pem_root_certs_cstr, &key_cert_pair,
179 NULL, NULL);
180 }
181 if (creds == NULL) {
182 rb_raise(rb_eRuntimeError,
183 "the call to grpc_ssl_credentials_create() failed, could not "
184 "create a credentials, see "
185 "https://github.com/grpc/grpc/blob/master/TROUBLESHOOTING.md for "
186 "debugging tips");
187 return Qnil;
188 }
189 wrapper->wrapped = creds;
190
191 /* Add the input objects as hidden fields to preserve them. */
192 rb_ivar_set(self, id_pem_cert_chain, pem_cert_chain);
193 rb_ivar_set(self, id_pem_private_key, pem_private_key);
194 rb_ivar_set(self, id_pem_root_certs, pem_root_certs);
195
196 return self;
197 }
198
grpc_rb_channel_credentials_compose(int argc,VALUE * argv,VALUE self)199 static VALUE grpc_rb_channel_credentials_compose(int argc, VALUE* argv,
200 VALUE self) {
201 grpc_channel_credentials* creds;
202 grpc_call_credentials* other;
203 grpc_channel_credentials* prev = NULL;
204 VALUE mark;
205 if (argc == 0) {
206 return self;
207 }
208 mark = rb_ary_new();
209 rb_ary_push(mark, self);
210 creds = grpc_rb_get_wrapped_channel_credentials(self);
211 for (int i = 0; i < argc; i++) {
212 rb_ary_push(mark, argv[i]);
213 other = grpc_rb_get_wrapped_call_credentials(argv[i]);
214 creds = grpc_composite_channel_credentials_create(creds, other, NULL);
215 if (prev != NULL) {
216 grpc_channel_credentials_release(prev);
217 }
218 prev = creds;
219
220 if (creds == NULL) {
221 rb_raise(rb_eRuntimeError,
222 "Failed to compose channel and call credentials");
223 }
224 }
225 return grpc_rb_wrap_channel_credentials(creds, mark);
226 }
227
get_ssl_roots_override(char ** pem_root_certs_ptr)228 static grpc_ssl_roots_override_result get_ssl_roots_override(
229 char** pem_root_certs_ptr) {
230 *pem_root_certs_ptr = pem_root_certs;
231 if (pem_root_certs == NULL) {
232 return GRPC_SSL_ROOTS_OVERRIDE_FAIL;
233 } else {
234 return GRPC_SSL_ROOTS_OVERRIDE_OK;
235 }
236 }
237
grpc_rb_set_default_roots_pem(VALUE self,VALUE roots)238 static VALUE grpc_rb_set_default_roots_pem(VALUE self, VALUE roots) {
239 char* roots_ptr = StringValueCStr(roots);
240 size_t length = strlen(roots_ptr);
241 (void)self;
242 pem_root_certs = gpr_malloc((length + 1) * sizeof(char));
243 memcpy(pem_root_certs, roots_ptr, length + 1);
244 return Qnil;
245 }
246
Init_grpc_channel_credentials()247 void Init_grpc_channel_credentials() {
248 grpc_rb_cChannelCredentials = rb_define_class_under(
249 grpc_rb_mGrpcCore, "ChannelCredentials", rb_cObject);
250
251 /* Allocates an object managed by the ruby runtime */
252 rb_define_alloc_func(grpc_rb_cChannelCredentials,
253 grpc_rb_channel_credentials_alloc);
254
255 /* Provides a ruby constructor and support for dup/clone. */
256 rb_define_method(grpc_rb_cChannelCredentials, "initialize",
257 grpc_rb_channel_credentials_init, -1);
258 rb_define_method(grpc_rb_cChannelCredentials, "initialize_copy",
259 grpc_rb_cannot_init_copy, 1);
260 rb_define_method(grpc_rb_cChannelCredentials, "compose",
261 grpc_rb_channel_credentials_compose, -1);
262 rb_define_module_function(grpc_rb_cChannelCredentials,
263 "set_default_roots_pem",
264 grpc_rb_set_default_roots_pem, 1);
265
266 grpc_set_ssl_roots_override_callback(get_ssl_roots_override);
267
268 id_pem_cert_chain = rb_intern("__pem_cert_chain");
269 id_pem_private_key = rb_intern("__pem_private_key");
270 id_pem_root_certs = rb_intern("__pem_root_certs");
271 }
272
273 /* Gets the wrapped grpc_channel_credentials from the ruby wrapper */
grpc_rb_get_wrapped_channel_credentials(VALUE v)274 grpc_channel_credentials* grpc_rb_get_wrapped_channel_credentials(VALUE v) {
275 grpc_rb_channel_credentials* wrapper = NULL;
276 Check_TypedStruct(v, &grpc_rb_channel_credentials_data_type);
277 TypedData_Get_Struct(v, grpc_rb_channel_credentials,
278 &grpc_rb_channel_credentials_data_type, wrapper);
279 return wrapper->wrapped;
280 }
281
282 /* Check if v is kind of ChannelCredentials */
grpc_rb_is_channel_credentials(VALUE v)283 bool grpc_rb_is_channel_credentials(VALUE v) {
284 return rb_typeddata_is_kind_of(v, &grpc_rb_channel_credentials_data_type);
285 }
286