xref: /aosp_15_r20/external/aws-crt-java/src/native/tls_context_options.c (revision 3c7ae9de214676c52d19f01067dc1a404272dc11)
1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include <jni.h>
7 
8 #include <aws/common/string.h>
9 #include <aws/io/tls_channel_handler.h>
10 
11 #include "crt.h"
12 #include "java_class_ids.h"
13 #include "tls_context_pkcs11_options.h"
14 
15 #include "custom_key_op_handler.h"
16 
17 /* Have to wrap the native struct so we can manage string lifetime */
18 struct jni_tls_ctx_options {
19     /* Must be first thing in the structure so casts to aws_tls_ctx_options work */
20     struct aws_tls_ctx_options options;
21     /* these strings get copied from java, so we don't have to pin and track references */
22     struct aws_string *ca_file;
23     struct aws_string *ca_path;
24     struct aws_string *alpn_list;
25     struct aws_string *certificate_path;
26     struct aws_string *private_key_path;
27     struct aws_string *pkcs12_path;
28     struct aws_string *pkcs12_password;
29     struct aws_string *certificate;
30     struct aws_string *private_key;
31     struct aws_string *windows_cert_store_path;
32     struct aws_string *ca_root;
33 
34     struct aws_tls_ctx_pkcs11_options *pkcs11_options;
35 
36     struct aws_custom_key_op_handler *custom_key_op_handler;
37 };
38 
39 /* on 32-bit platforms, casting pointers to longs throws a warning we don't need */
40 #if UINTPTR_MAX == 0xffffffff
41 #    if defined(_MSC_VER)
42 #        pragma warning(push)
43 #        pragma warning(disable : 4305) /* 'type cast': truncation from 'jlong' to 'jni_tls_ctx_options *' */
44 #    else
45 #        pragma GCC diagnostic push
46 #        pragma GCC diagnostic ignored "-Wpointer-to-int-cast"
47 #        pragma GCC diagnostic ignored "-Wint-to-pointer-cast"
48 #    endif
49 #endif
50 
s_jni_tls_ctx_options_destroy(struct jni_tls_ctx_options * tls)51 static void s_jni_tls_ctx_options_destroy(struct jni_tls_ctx_options *tls) {
52     if (tls == NULL) {
53         return;
54     }
55 
56     aws_string_destroy(tls->ca_file);
57     aws_string_destroy(tls->ca_path);
58     aws_string_destroy(tls->alpn_list);
59     aws_string_destroy(tls->certificate_path);
60     aws_string_destroy(tls->private_key_path);
61     aws_string_destroy(tls->pkcs12_path);
62     aws_string_destroy_secure(tls->pkcs12_password);
63     aws_string_destroy(tls->certificate);
64     aws_string_destroy_secure(tls->private_key);
65     aws_string_destroy(tls->windows_cert_store_path);
66     aws_string_destroy(tls->ca_root);
67 
68     aws_tls_ctx_pkcs11_options_from_java_destroy(tls->pkcs11_options);
69     aws_custom_key_op_handler_java_release(tls->custom_key_op_handler);
70     aws_tls_ctx_options_clean_up(&tls->options);
71 
72     struct aws_allocator *allocator = aws_jni_get_allocator();
73     aws_mem_release(allocator, tls);
74 }
75 
76 JNIEXPORT
Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsNew(JNIEnv * env,jclass jni_class,jint jni_min_tls_version,jint jni_cipher_pref,jstring jni_alpn,jstring jni_certificate,jstring jni_private_key,jstring jni_cert_path,jstring jni_key_path,jstring jni_ca,jstring jni_ca_filepath,jstring jni_ca_dirpath,jboolean jni_verify_peer,jstring jni_pkcs12_path,jstring jni_pkcs12_password,jobject jni_pkcs11_options,jobject jni_custom_key_op,jstring jni_windows_cert_store_path)77 jlong JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsNew(
78     JNIEnv *env,
79     jclass jni_class,
80     jint jni_min_tls_version,
81     jint jni_cipher_pref,
82     jstring jni_alpn,
83     jstring jni_certificate,
84     jstring jni_private_key,
85     jstring jni_cert_path,
86     jstring jni_key_path,
87     jstring jni_ca,
88     jstring jni_ca_filepath,
89     jstring jni_ca_dirpath,
90     jboolean jni_verify_peer,
91     jstring jni_pkcs12_path,
92     jstring jni_pkcs12_password,
93     jobject jni_pkcs11_options,
94     jobject jni_custom_key_op,
95     jstring jni_windows_cert_store_path) {
96     (void)jni_class;
97     aws_cache_jni_ids(env);
98 
99     struct aws_allocator *allocator = aws_jni_get_allocator();
100     struct jni_tls_ctx_options *tls = aws_mem_calloc(allocator, 1, sizeof(struct jni_tls_ctx_options));
101     AWS_FATAL_ASSERT(tls);
102     aws_tls_ctx_options_init_default_client(&tls->options, allocator);
103 
104     /* Certs or paths will cause an init, which overwrites other fields, so do those first */
105     if (jni_certificate && jni_private_key) {
106         tls->certificate = aws_jni_new_string_from_jstring(env, jni_certificate);
107         if (!tls->certificate) {
108             aws_jni_throw_runtime_exception(env, "failed to get certificate string");
109             goto on_error;
110         }
111         tls->private_key = aws_jni_new_string_from_jstring(env, jni_private_key);
112         if (!tls->private_key) {
113             aws_jni_throw_runtime_exception(env, "failed to get privateKey string");
114             goto on_error;
115         }
116 
117         struct aws_byte_cursor cert_cursor = aws_byte_cursor_from_string(tls->certificate);
118         struct aws_byte_cursor key_cursor = aws_byte_cursor_from_string(tls->private_key);
119 
120         if (aws_tls_ctx_options_init_client_mtls(&tls->options, allocator, &cert_cursor, &key_cursor)) {
121             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls failed");
122             goto on_error;
123         }
124     } else if (jni_cert_path && jni_key_path) {
125         tls->certificate_path = aws_jni_new_string_from_jstring(env, jni_cert_path);
126         if (!tls->certificate_path) {
127             aws_jni_throw_runtime_exception(env, "failed to get certificatePath string");
128             goto on_error;
129         }
130         tls->private_key_path = aws_jni_new_string_from_jstring(env, jni_key_path);
131         if (!tls->private_key_path) {
132             aws_jni_throw_runtime_exception(env, "failed to get privateKeyPath string");
133             goto on_error;
134         }
135 
136         if (aws_tls_ctx_options_init_client_mtls_from_path(
137                 &tls->options,
138                 allocator,
139                 aws_string_c_str(tls->certificate_path),
140                 aws_string_c_str(tls->private_key_path))) {
141             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_from_path failed");
142             goto on_error;
143         }
144     } else if (jni_pkcs11_options) {
145         tls->pkcs11_options = aws_tls_ctx_pkcs11_options_from_java_new(env, jni_pkcs11_options);
146         if (tls->pkcs11_options == NULL) {
147             /* exception already thrown */
148             goto on_error;
149         }
150 
151         if (aws_tls_ctx_options_init_client_mtls_with_pkcs11(&tls->options, allocator, tls->pkcs11_options)) {
152             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_with_pkcs11 failed");
153             goto on_error;
154         }
155     } else if (jni_custom_key_op) {
156 
157         jobject jni_custom_key_op_handle = (*env)->GetObjectField(
158             env, jni_custom_key_op, tls_context_custom_key_operation_options_properties.operation_handler_field_id);
159         if (!jni_custom_key_op_handle) {
160             aws_jni_throw_runtime_exception(
161                 env, "could not get custom operation handler from jni_custom_key_op_handle!");
162             goto on_error;
163         }
164 
165         tls->custom_key_op_handler = aws_custom_key_op_handler_java_new(env, jni_custom_key_op_handle);
166 
167         /* Certificate needs to be set, but there are multiple ways to get it */
168         jstring jni_custom_key_op_cert_path = (*env)->GetObjectField(
169             env, jni_custom_key_op, tls_context_custom_key_operation_options_properties.certificate_file_path_field_id);
170         jstring jni_custom_key_op_cert_contents = (*env)->GetObjectField(
171             env,
172             jni_custom_key_op,
173             tls_context_custom_key_operation_options_properties.certificate_file_contents_field_id);
174 
175         if (jni_custom_key_op_cert_path && jni_custom_key_op_cert_contents) {
176             /* Cannot have both a certificate path and data - use one or the other */
177             aws_jni_throw_runtime_exception(
178                 env, "Custom key operation handler: cannot have both certificate file path and certificate contents!");
179             goto on_error;
180         } else if (jni_custom_key_op_cert_contents) {
181             /* If we have the certificate contents, then use it directly */
182             tls->certificate = aws_jni_new_string_from_jstring(env, jni_custom_key_op_cert_contents);
183             if (!tls->certificate) {
184                 aws_jni_throw_runtime_exception(
185                     env, "Custom key operation handler: failed to get certificate contents string");
186                 goto on_error;
187             }
188             struct aws_byte_cursor certificate_byte_cursor = aws_byte_cursor_from_string(tls->certificate);
189 
190             /* Initialize the client with a custom key operation */
191             if (aws_tls_ctx_options_init_client_mtls_with_custom_key_operations(
192                     &tls->options, allocator, tls->custom_key_op_handler, &certificate_byte_cursor) != AWS_OP_SUCCESS) {
193                 aws_jni_throw_runtime_exception(
194                     env, "aws_tls_ctx_options_init_client_mtls_with_custom_key_operations failed");
195                 goto on_error;
196             }
197         } else if (jni_custom_key_op_cert_path) {
198             /* If we have a certificate path, we need to get the certificate data from it and use that */
199             tls->certificate_path = aws_jni_new_string_from_jstring(env, jni_custom_key_op_cert_path);
200             if (!tls->certificate_path) {
201                 aws_jni_throw_runtime_exception(
202                     env, "Custom key operation handler: failed to get certificate path string");
203                 goto on_error;
204             }
205             struct aws_byte_buf tmp_byte_buf;
206             int op = aws_byte_buf_init_from_file(&tmp_byte_buf, allocator, aws_string_c_str(tls->certificate_path));
207             if (op != AWS_OP_SUCCESS) {
208                 aws_jni_throw_runtime_exception(
209                     env, "Custom key operation handler: failed to get certificate path string");
210                 aws_byte_buf_clean_up(&tmp_byte_buf);
211                 goto on_error;
212             }
213             struct aws_byte_cursor certificate_byte_cursor = aws_byte_cursor_from_buf(&tmp_byte_buf);
214 
215             /* Initialize the client with a custom key operation */
216             if (aws_tls_ctx_options_init_client_mtls_with_custom_key_operations(
217                     &tls->options, allocator, tls->custom_key_op_handler, &certificate_byte_cursor) != AWS_OP_SUCCESS) {
218                 aws_jni_throw_runtime_exception(
219                     env, "aws_tls_ctx_options_init_client_mtls_with_custom_key_operations failed");
220                 aws_byte_buf_clean_up(&tmp_byte_buf);
221                 goto on_error;
222             }
223             aws_byte_buf_clean_up(&tmp_byte_buf);
224         } else {
225             aws_jni_throw_runtime_exception(env, "Custom key operation handler: No certificate set!");
226             goto on_error;
227         }
228 
229     } else if (jni_pkcs12_path && jni_pkcs12_password) {
230         tls->pkcs12_path = aws_jni_new_string_from_jstring(env, jni_pkcs12_path);
231         if (!tls->pkcs12_path) {
232             aws_jni_throw_runtime_exception(env, "failed to get pkcs12Path string");
233             goto on_error;
234         }
235         tls->pkcs12_password = aws_jni_new_string_from_jstring(env, jni_pkcs12_password);
236         if (!tls->pkcs12_password) {
237             aws_jni_throw_runtime_exception(env, "failed to get pkcs12Password string");
238             goto on_error;
239         }
240 
241         struct aws_byte_cursor password = aws_byte_cursor_from_string(tls->pkcs12_password);
242         if (aws_tls_ctx_options_init_client_mtls_pkcs12_from_path(
243                 &tls->options, allocator, aws_string_c_str(tls->pkcs12_path), &password)) {
244             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_pkcs12_from_path failed");
245             goto on_error;
246         }
247     } else if (jni_windows_cert_store_path) {
248         tls->windows_cert_store_path = aws_jni_new_string_from_jstring(env, jni_windows_cert_store_path);
249         if (!tls->windows_cert_store_path) {
250             aws_jni_throw_runtime_exception(env, "failed to get windowsCertStorePath string");
251             goto on_error;
252         }
253 
254         if (aws_tls_ctx_options_init_client_mtls_from_system_path(
255                 &tls->options, allocator, aws_string_c_str(tls->windows_cert_store_path))) {
256 
257             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_init_client_mtls_from_system_path failed");
258             goto on_error;
259         }
260     }
261 
262     if (jni_ca) {
263         tls->ca_root = aws_jni_new_string_from_jstring(env, jni_ca);
264         if (!tls->ca_root) {
265             aws_jni_throw_runtime_exception(env, "failed to get caRoot string");
266             goto on_error;
267         }
268         struct aws_byte_cursor ca_cursor = aws_byte_cursor_from_string(tls->ca_root);
269         if (aws_tls_ctx_options_override_default_trust_store(&tls->options, &ca_cursor)) {
270             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_override_default_trust_store failed");
271             goto on_error;
272         }
273     } else if (jni_ca_filepath || jni_ca_dirpath) {
274         const char *ca_file = NULL;
275         const char *ca_path = NULL;
276         if (jni_ca_filepath) {
277             tls->ca_file = aws_jni_new_string_from_jstring(env, jni_ca_filepath);
278             if (!tls->ca_file) {
279                 aws_jni_throw_runtime_exception(env, "failed to get caFile string");
280                 goto on_error;
281             }
282 
283             ca_file = aws_string_c_str(tls->ca_file);
284         }
285         if (jni_ca_dirpath) {
286             tls->ca_path = aws_jni_new_string_from_jstring(env, jni_ca_dirpath);
287             if (!tls->ca_path) {
288                 aws_jni_throw_runtime_exception(env, "failed to get caPath string");
289                 goto on_error;
290             }
291 
292             ca_path = aws_string_c_str(tls->ca_path);
293         }
294 
295         if (aws_tls_ctx_options_override_default_trust_store_from_path(&tls->options, ca_path, ca_file)) {
296             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_override_default_trust_store_from_path failed");
297             goto on_error;
298         }
299     }
300 
301     /* apply the rest of the non-init settings */
302     tls->options.minimum_tls_version = (enum aws_tls_versions)jni_min_tls_version;
303     tls->options.cipher_pref = (enum aws_tls_cipher_pref)jni_cipher_pref;
304     tls->options.verify_peer = jni_verify_peer != 0;
305 
306     if (jni_alpn) {
307         tls->alpn_list = aws_jni_new_string_from_jstring(env, jni_alpn);
308         if (!tls->alpn_list) {
309             aws_jni_throw_runtime_exception(env, "failed to get alpnList string");
310             goto on_error;
311         }
312 
313         if (aws_tls_ctx_options_set_alpn_list(&tls->options, aws_string_c_str(tls->alpn_list))) {
314             aws_jni_throw_runtime_exception(env, "aws_tls_ctx_options_set_alpn_list failed");
315             goto on_error;
316         }
317     }
318 
319     return (jlong)tls;
320 
321 on_error:
322 
323     s_jni_tls_ctx_options_destroy(tls);
324 
325     return (jlong)0;
326 }
327 
328 JNIEXPORT
Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsDestroy(JNIEnv * env,jclass jni_class,jlong jni_tls)329 void JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsDestroy(
330     JNIEnv *env,
331     jclass jni_class,
332     jlong jni_tls) {
333     (void)jni_class;
334     aws_cache_jni_ids(env);
335 
336     s_jni_tls_ctx_options_destroy((struct jni_tls_ctx_options *)jni_tls);
337 }
338 
339 JNIEXPORT
Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsAlpnAvailable(JNIEnv * env,jclass jni_class)340 jboolean JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsAlpnAvailable(
341     JNIEnv *env,
342     jclass jni_class) {
343     (void)jni_class;
344     aws_cache_jni_ids(env);
345 
346     return aws_tls_is_alpn_available();
347 }
348 
349 JNIEXPORT
Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsCipherPreferenceSupported(JNIEnv * env,jclass jni_class,jint jni_cipher_pref)350 jboolean JNICALL Java_software_amazon_awssdk_crt_io_TlsContextOptions_tlsContextOptionsIsCipherPreferenceSupported(
351     JNIEnv *env,
352     jclass jni_class,
353     jint jni_cipher_pref) {
354 
355     (void)jni_class;
356     aws_cache_jni_ids(env);
357 
358     if (jni_cipher_pref < 0 || AWS_IO_TLS_CIPHER_PREF_END_RANGE <= jni_cipher_pref) {
359         aws_jni_throw_runtime_exception(
360             env,
361             "TlsContextOptions.tlsContextOptionsSetCipherPreference: TlsCipherPreference is out of range: %d",
362             (int)jni_cipher_pref);
363         return false;
364     }
365 
366     return aws_tls_is_cipher_pref_supported((enum aws_tls_cipher_pref)jni_cipher_pref);
367 }
368 
369 #if UINTPTR_MAX == 0xffffffff
370 #    if defined(_MSC_VER)
371 #        pragma warning(pop)
372 #    else
373 #        pragma GCC diagnostic pop
374 #    endif
375 #endif
376