xref: /aosp_15_r20/external/grpc-grpc/src/ruby/ext/grpc/rb_channel_credentials.c (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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