From 1715f09d85846041936494b89a16cc5149dc64c6 Mon Sep 17 00:00:00 2001 From: Adrian Cruceru Date: Mon, 30 Nov 2020 21:14:27 +0000 Subject: [PATCH] Moving from referene counting allows simpler move to native-tls / hyper. Arc Changes: - Each Config/Context/... will hold Arcs towards items it holds pointers to. - This forces objects to live as long as needed, once no longer used they get destroyed by reference counting. This allows passing the objects to multiple threads without worrying about lifetime. I've also added notes why classes are Sync where used. Let me know if I missed any classes. Usage example of an intermediate mbed-hyper integration is at: - https://github.com/fortanix/rust-mbedtls/tree/acruceru/wip-mbed-hyper-v2/mbedtls-hyper/examples/integrations There I added a crate to wrap hyper - similar to native-tls. (that will be moved to native-tls layer soon) That crate can be considered an integration test that I will raise a separate PR for. Changes after initial review: - Added forward_mbedtls_calloc / forward_mbedtls_free functions so we can pass certificates to and from mbedtls without allocator mismatches/corruptions. - Switched to MbedtlsList and Certificate. A MbedtlsBox is pending for this PR as well. - Fixed most comments. Still pending: - Update define! macros - Add MbedtlsBox --- Cargo.lock | 164 +++++ mbedtls-sys/Cargo.toml | 1 + mbedtls-sys/build/cmake.rs | 3 + mbedtls/Cargo.toml | 9 + mbedtls/build.rs | 6 + mbedtls/examples/client.rs | 21 +- mbedtls/examples/server.rs | 23 +- mbedtls/src/alloc.rs | 60 ++ mbedtls/src/lib.rs | 9 + mbedtls/src/pk/mod.rs | 92 ++- mbedtls/src/pk/rfc6979.rs | 10 +- mbedtls/src/pkcs12/mod.rs | 3 +- mbedtls/src/rng/ctr_drbg.rs | 212 +++--- mbedtls/src/rng/hmac_drbg.rs | 67 +- mbedtls/src/rng/mod.rs | 6 +- mbedtls/src/rng/os_entropy.rs | 80 ++- mbedtls/src/rng/rdrand.rs | 35 +- mbedtls/src/rust_malloc.c | 31 + mbedtls/src/self_test.rs | 7 +- mbedtls/src/ssl/ciphersuites.rs | 1 + mbedtls/src/ssl/config.rs | 568 +++++++++------ mbedtls/src/ssl/context.rs | 358 ++++++---- mbedtls/src/ssl/mod.rs | 4 +- mbedtls/src/ssl/ticket.rs | 52 +- mbedtls/src/wrapper_macros.rs | 133 +++- mbedtls/src/x509/certificate.rs | 1133 ++++++++++++++++++++++-------- mbedtls/src/x509/mod.rs | 2 +- mbedtls/tests/client_server.rs | 89 ++- mbedtls/tests/hyper.rs | 525 ++++++++++++++ mbedtls/tests/ssl_conf_ca_cb.rs | 50 +- mbedtls/tests/ssl_conf_verify.rs | 47 +- mbedtls/tests/support/entropy.rs | 2 +- mbedtls/tests/support/rand.rs | 18 +- mbedtls/valgrind_unittests.sh | 6 + 34 files changed, 2835 insertions(+), 992 deletions(-) create mode 100644 mbedtls/src/alloc.rs create mode 100644 mbedtls/src/rust_malloc.c create mode 100644 mbedtls/tests/hyper.rs create mode 100755 mbedtls/valgrind_unittests.sh diff --git a/Cargo.lock b/Cargo.lock index faeb7fbea..441be4813 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13,6 +13,15 @@ name = "autocfg" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "base64" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "bindgen" version = "0.19.2" @@ -171,11 +180,52 @@ name = "glob" version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "hermit-abi" +version = "0.1.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "httparse" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "hyper" +version = "0.10.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)", + "httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)", + "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", + "traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "idna" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-normalization 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "kernel32-sys" version = "0.2.2" @@ -185,6 +235,11 @@ dependencies = [ "winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "language-tags" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "lazy_static" version = "0.2.11" @@ -238,6 +293,7 @@ dependencies = [ "chrono 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)", "core_io 0.1.20190701 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "mbedtls-sys-auto 2.24.0", @@ -277,6 +333,14 @@ dependencies = [ "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "mime" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "nom" version = "3.2.1" @@ -302,11 +366,25 @@ dependencies = [ "autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hermit-abi 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "opaque-debug" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "percent-encoding" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "pkg-config" version = "0.3.16" @@ -413,6 +491,11 @@ dependencies = [ "semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "safemem" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "semver" version = "0.1.20" @@ -543,11 +626,58 @@ dependencies = [ "winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tinyvec_macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "traitobject" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + +[[package]] +name = "typeable" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "typenum" version = "1.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "unicase" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "tinyvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "unicode-xid" version = "0.0.3" @@ -558,6 +688,16 @@ name = "unicode-xid" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "url" +version = "1.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "utf8-ranges" version = "0.1.3" @@ -568,6 +708,11 @@ name = "vcpkg" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "version_check" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "winapi" version = "0.2.8" @@ -605,6 +750,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [metadata] "checksum aho-corasick 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ca972c2ea5f742bfce5687b9aef75506a764f61d37f8f649047846a9686ddb66" "checksum autocfg 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b671c8fb71b457dd4ae18c4ba1e59aa81793daacc361d82fcd410cef0d491875" +"checksum base64 0.9.3 (registry+https://github.com/rust-lang/crates.io-index)" = "489d6c0ed21b11d038c31b6ceccca973e65d73ba3bd8ecb9a2babf5546164643" "checksum bindgen 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)" = "003f95e0fb6cf3d1fee8c62b2ec35135509477989fab15c358e0efa3972bdef6" "checksum bitflags 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "4f67931368edf3a9a51d29886d245f1c3db2f1ef0dcc9e35ff70341b78c10d23" "checksum bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "aad18937a628ec6abcd26d1489012cc0e18c21798210f491af69ded9b881106d" @@ -626,8 +772,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-cprng 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" "checksum generic-array 0.12.3 (registry+https://github.com/rust-lang/crates.io-index)" = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" +"checksum hermit-abi 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "5aca5565f760fb5b220e499d72710ed156fdb74e631659e99377d9ebfbd13ae8" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum httparse 1.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "cd179ae861f0c2e53da70d892f5f3029f9594be0c41dc5269cd371691b1dc2f9" +"checksum hyper 0.10.16 (registry+https://github.com/rust-lang/crates.io-index)" = "0a0652d9a2609a968c14be1a9ea00bf4b1d64e2e1f53a1b51b6fff3a6e829273" +"checksum idna 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "38f09e0f0b1fb55fdee1f17470ad800da77af5186a1a76c026b679358b7e844e" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +"checksum language-tags 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a91d884b6667cd606bb5a69aa0c99ba811a115fc68915e7056ec08a46e93199a" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum libc 0.2.62 (registry+https://github.com/rust-lang/crates.io-index)" = "34fcd2c08d2f832f376f4173a231990fa5aef4e99fb569867318a227ef4c06ba" "checksum libz-sys 1.0.25 (registry+https://github.com/rust-lang/crates.io-index)" = "2eb5e43362e38e2bca2fd5f5134c4d4564a23a5c28e9b95411652021a8675ebe" @@ -636,10 +787,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" "checksum memchr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d8b629fb514376c675b98c1421e80b151d3817ac42d7c667717d282761418d20" "checksum memchr 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "148fab2e51b4f1cfc66da2a7c32981d1d3c083a803978268bb11fe4b86925e7a" +"checksum mime 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "ba626b8a6de5da682e1caa06bdb42a335aee5a84db8e5046a3e8ab17ba0a3ae0" "checksum nom 3.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05aec50c70fd288702bcd93284a8444607f3292dbdf2a30de5ea5dcdbe72287b" "checksum num-integer 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "b85e541ef8255f6cf42bbfe4ef361305c6c135d10919ecc26126c4e5ae94bc09" "checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" +"checksum num_cpus 1.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" "checksum opaque-debug 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" +"checksum percent-encoding 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "31010dd2e1ac33d5b46a5b413495239882813e0369f8ed8a5e266f173602f831" "checksum pkg-config 0.3.16 (registry+https://github.com/rust-lang/crates.io-index)" = "72d5370d90f49f70bd033c3d75e87fc529fbfff9d6f7cccef07d6170079d91ea" "checksum proc-macro2 0.4.30 (registry+https://github.com/rust-lang/crates.io-index)" = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759" "checksum quote 0.6.13 (registry+https://github.com/rust-lang/crates.io-index)" = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1" @@ -654,6 +808,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rs-libc 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "80a671d6c4696a49b78e0a271c99bc58bc1a17a64893a3684a1ba1a944b26ca9" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum rustc_version 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "c5f5376ea5e30ce23c03eb77cbe4962b988deead10910c372b226388b594c084" +"checksum safemem 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "ef703b7cb59335eae2eb93ceb664c0eb7ea6bf567079d843e09420219668e072" "checksum semver 0.1.20 (registry+https://github.com/rust-lang/crates.io-index)" = "d4f410fedcf71af0345d7607d246e7ad15faaadd49d240ee3b24e5dc21a820ac" "checksum serde 1.0.101 (registry+https://github.com/rust-lang/crates.io-index)" = "9796c9b7ba2ffe7a9ce53c2287dfc48080f4b2b362fcc245a259b3a7201119dd" "checksum serde_bytes 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "defbb8a83d7f34cc8380751eeb892b825944222888aff18996ea7901f24aec88" @@ -669,11 +824,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" "checksum thread_local 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "8576dbbfcaef9641452d5cf0df9b0e7eeab7694956dd33bb61515fb8f18cfdd5" "checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f" +"checksum tinyvec 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +"checksum tinyvec_macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" +"checksum traitobject 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "efd1f82c56340fdf16f2a953d7bda4f8fdffba13d93b00844c25572110b26079" +"checksum typeable 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1410f6f91f21d1612654e7cc69193b0334f909dcf2c790c4826254fbb86f8887" "checksum typenum 1.11.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6d2783fe2d6b8c1101136184eb41be8b1ad379e4657050b8aaff0c79ee7575f9" +"checksum unicase 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7f4765f83163b74f957c797ad9253caf97f103fb064d3999aea9568d09fc8a33" +"checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +"checksum unicode-normalization 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)" = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" "checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" +"checksum url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "dd4e7c0d531266369519a4aa4f399d748bd37043b00bde1e4ff1f60a120b355a" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" "checksum vcpkg 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "33dd455d0f96e90a75803cfeb7f948768c08d70a6de9a8d2362461935698bf95" +"checksum version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" diff --git a/mbedtls-sys/Cargo.toml b/mbedtls-sys/Cargo.toml index 6b1ab3505..90d90ce57 100644 --- a/mbedtls-sys/Cargo.toml +++ b/mbedtls-sys/Cargo.toml @@ -12,6 +12,7 @@ This version generates the correct bindings at compile time using bindgen.""" readme = "../README.md" repository = "https://github.com/fortanix/rust-mbedtls" documentation = "https://docs.rs/mbedtls-sys-auto/" +links = "mbedtls" [lib] name = "mbedtls_sys" diff --git a/mbedtls-sys/build/cmake.rs b/mbedtls-sys/build/cmake.rs index 673ed3903..767684ba1 100644 --- a/mbedtls-sys/build/cmake.rs +++ b/mbedtls-sys/build/cmake.rs @@ -49,5 +49,8 @@ impl super::BuildConfig { println!("cargo:rustc-link-lib=mbedtls"); println!("cargo:rustc-link-lib=mbedx509"); println!("cargo:rustc-link-lib=mbedcrypto"); + + println!("cargo:include={}/{}", ::std::env::current_dir().unwrap().display(), self.mbedtls_src.join("include").display()); + println!("cargo:config_h={}", self.config_h.to_str().expect("config.h UTF-8 error")); } } diff --git a/mbedtls/Cargo.toml b/mbedtls/Cargo.toml index f9605a243..ced7080c0 100644 --- a/mbedtls/Cargo.toml +++ b/mbedtls/Cargo.toml @@ -38,12 +38,15 @@ default-features = false features = ["custom_printf", "trusted_cert_callback"] path = "../mbedtls-sys" + + [dev-dependencies] libc = "0.2.0" rand = "0.4.0" serde_cbor = "0.6" hex = "0.3" matches = "0.1.8" +hyper = { version = "0.10.16", default-features = false } [build-dependencies] cc = "1.0" @@ -119,3 +122,9 @@ required-features = ["std"] name = "ssl_conf_verify" path = "tests/ssl_conf_verify.rs" required-features = ["std"] + + +[[test]] +name = "hyper" +path = "tests/hyper.rs" +required-features = ["default", "pthread"] diff --git a/mbedtls/build.rs b/mbedtls/build.rs index 004c8a9c1..1d7ba4b39 100644 --- a/mbedtls/build.rs +++ b/mbedtls/build.rs @@ -12,6 +12,12 @@ use std::env; fn main() { let mut b = cc::Build::new(); + b.include(env::var_os("DEP_MBEDTLS_INCLUDE").expect("Links was not properly set in mbedtls-sys package, missing DEP_MBEDTLS_INCLUDE")); + let config_file = format!("\"{}\"", env::var_os("DEP_MBEDTLS_CONFIG_H").expect("Links was not properly set in mbedtls-sys package, missing DEP_MBEDTLS_CONFIG_H").to_str().unwrap()); + b.define("MBEDTLS_CONFIG_FILE", + Some(config_file.as_str())); + + b.file("src/rust_malloc.c"); b.file("src/rust_printf.c"); if env::var_os("CARGO_FEATURE_STD").is_none() || ::std::env::var("TARGET") diff --git a/mbedtls/examples/client.rs b/mbedtls/examples/client.rs index 9dcb907a6..18de85ba7 100644 --- a/mbedtls/examples/client.rs +++ b/mbedtls/examples/client.rs @@ -10,6 +10,7 @@ extern crate mbedtls; use std::io::{self, stdin, stdout, Write}; use std::net::TcpStream; +use std::sync::Arc; use mbedtls::rng::CtrDrbg; use mbedtls::ssl::config::{Endpoint, Preset, Transport}; @@ -23,21 +24,21 @@ use support::entropy::entropy_new; use support::keys; fn result_main(addr: &str) -> TlsResult<()> { - let mut entropy = entropy_new(); - let mut rng = CtrDrbg::new(&mut entropy, None)?; - let mut cert = Certificate::from_pem(keys::ROOT_CA_CERT)?; + let entropy = Arc::new(entropy_new()); + let rng = Arc::new(CtrDrbg::new(entropy, None)?); + let cert = Arc::new(Certificate::from_pem_multiple(keys::PEM_CERT)?); let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default); - config.set_rng(Some(&mut rng)); - config.set_ca_list(Some(&mut *cert), None); - let mut ctx = Context::new(&config)?; + config.set_rng(rng); + config.set_ca_list(cert, None); + let mut ctx = Context::new(Arc::new(config)); - let mut conn = TcpStream::connect(addr).unwrap(); - let mut session = ctx.establish(&mut conn, None)?; + let conn = TcpStream::connect(addr).unwrap(); + ctx.establish(conn, None)?; let mut line = String::new(); stdin().read_line(&mut line).unwrap(); - session.write_all(line.as_bytes()).unwrap(); - io::copy(&mut session, &mut stdout()).unwrap(); + ctx.write_all(line.as_bytes()).unwrap(); + io::copy(&mut ctx, &mut stdout()).unwrap(); Ok(()) } diff --git a/mbedtls/examples/server.rs b/mbedtls/examples/server.rs index 59f8d7597..1ff5c5a5a 100644 --- a/mbedtls/examples/server.rs +++ b/mbedtls/examples/server.rs @@ -10,6 +10,7 @@ extern crate mbedtls; use std::io::{BufRead, BufReader, Write}; use std::net::{TcpListener, TcpStream}; +use std::sync::Arc; use mbedtls::pk::Pk; use mbedtls::rng::CtrDrbg; @@ -29,21 +30,25 @@ fn listen Result<(), E>>(mut handle_client: F) -> Resu println!("Connection from {}", conn.peer_addr().unwrap()); handle_client(conn)?; } + Ok(()) } fn result_main() -> TlsResult<()> { - let mut entropy = entropy_new(); - let mut rng = CtrDrbg::new(&mut entropy, None)?; - let mut cert = Certificate::from_pem(keys::PEM_CERT)?; - let mut key = Pk::from_private_key(keys::PEM_KEY, None)?; + let entropy = entropy_new(); + let rng = Arc::new(CtrDrbg::new(Arc::new(entropy), None)?); + let cert = Arc::new(Certificate::from_pem_multiple(keys::PEM_CERT)?); + let key = Arc::new(Pk::from_private_key(keys::PEM_KEY, None)?); let mut config = Config::new(Endpoint::Server, Transport::Stream, Preset::Default); - config.set_rng(Some(&mut rng)); - config.push_cert(&mut *cert, &mut key)?; - let mut ctx = Context::new(&config)?; + config.set_rng(rng); + config.push_cert(cert, key)?; + + let rc_config = Arc::new(config); - listen(|mut conn| { - let mut session = BufReader::new(ctx.establish(&mut conn, None)?); + listen(move |conn| { + let mut ctx = Context::new(rc_config.clone()); + ctx.establish(conn, None)?; + let mut session = BufReader::new(ctx); let mut line = Vec::new(); session.read_until(b'\n', &mut line).unwrap(); session.get_mut().write_all(&line).unwrap(); diff --git a/mbedtls/src/alloc.rs b/mbedtls/src/alloc.rs new file mode 100644 index 000000000..c836afa4e --- /dev/null +++ b/mbedtls/src/alloc.rs @@ -0,0 +1,60 @@ +/* Copyright (c) Fortanix, Inc. + * + * Licensed under the GNU General Public License, version 2 or the Apache License, Version + * 2.0 , at your + * option. This file may not be copied, modified, or distributed except + * according to those terms. */ + +#[cfg(not(feature = "std"))] +use crate::alloc_prelude::*; + +use core::fmt; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; +use core::ptr::drop_in_place; + +use mbedtls_sys::types::raw_types::c_void; + +extern "C" { + pub fn forward_mbedtls_free(n: *mut mbedtls_sys::types::raw_types::c_void); +} + +#[repr(transparent)] +pub struct Box { + pub(crate) inner: NonNull +} + +impl Deref for Box { + type Target = T; + fn deref(&self) -> &T { + unsafe { self.inner.as_ref() } + } +} + +impl DerefMut for Box { + fn deref_mut(&mut self) -> &mut T { + unsafe { self.inner.as_mut() } + } +} + +impl fmt::Debug for Box { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +impl Drop for Box { + fn drop(&mut self) { + unsafe { + drop_in_place(self.inner.as_ptr()); + forward_mbedtls_free(self.inner.as_ptr() as *mut c_void) + } + } +} + +#[repr(transparent)] +pub struct List { + pub(crate) inner: Option> +} + diff --git a/mbedtls/src/lib.rs b/mbedtls/src/lib.rs index a5e15df43..59eda7bab 100644 --- a/mbedtls/src/lib.rs +++ b/mbedtls/src/lib.rs @@ -59,6 +59,7 @@ pub mod rng; pub mod self_test; pub mod ssl; pub mod x509; +pub mod alloc; #[cfg(feature = "pkcs12")] pub mod pkcs12; @@ -163,3 +164,11 @@ pub unsafe extern "C" fn mbedtls_time(tp: *mut time_t) -> time_t { } timestamp } + +// Debug not available in SGX +#[cfg(not(target_env = "sgx"))] +pub unsafe fn set_global_debug_threshold(threshold: i32) { + mbedtls_sys::debug_set_threshold(threshold); +} + + diff --git a/mbedtls/src/pk/mod.rs b/mbedtls/src/pk/mod.rs index 28cb6086f..3676b4a2c 100644 --- a/mbedtls/src/pk/mod.rs +++ b/mbedtls/src/pk/mod.rs @@ -132,6 +132,7 @@ const CUSTOM_PK_INFO: pk_info_t = { } }; +// If this changes then certificate.rs unsafe code in public_key needs to also change. define!( #[c_ty(pk_context)] #[repr(C)] @@ -142,6 +143,91 @@ define!( impl<'a> UnsafeFrom {} ); +// # Safety +// +// Thread safety analysis for Pk. +// +// A. Usage example of Pk. +// +// 1.1. Common use case is to to pass it as parameter to the SSL Config class. +// 1.2. SSL Config class is then used by multiple Context classes (one for each connection) +// 1.3. Context classes, handled by different threads will do calls towards Pk. +// +// Since this is a common use case for MbedTLS it should be thread safe if threading is enabled. +// +// B. Verifying thread safety. +// +// 1. Calls towards the specific Pk implementation are done via function pointers. +// +// - Example call towards Pk: +// ../../../mbedtls-sys/vendor/library/ssl_srv.c:3707 - mbedtls_pk_decrypt( private_key, p, len, ... +// - This calls a generic function pointer via: +// ../../../mbedtls-sys/vendor/crypto/library/pk.c:475 - return( ctx->pk_info->decrypt_func( ctx->pk_ctx, input, ilen, +// +// 2. Pk implementation types. +// +// - The function pointers are defined via function: +// ../../../mbedtls-sys/vendor/crypto/library/pk.c:115 - mbedtls_pk_info_from_type +// - They are as follows: mbedtls_rsa_info / mbedtls_eckey_info / mbedtls_ecdsa_info +// - These are defined in: +// ../../../mbedtls-sys/vendor/crypto/library/pk_wrap.c:196 +// +// C. Checking types one by one. +// +// 1. RSA: mbedtls_rsa_info at ../../../mbedtls-sys/vendor/crypto/library/pk_wrap.c:196 +// This uses internal locks in: ../../../mbedtls-sys/vendor/crypto/library/rsa.c:718 +// +// 2. ECKEY: mbedtls_eckey_info at ../../../mbedtls-sys/vendor/crypto/library/pk_wrap.c:418 +// This does not use internal locks but avoids interior mutability. +// +// Function checks one by one: +// - Only const access to context: eckey_check_pair, eckey_get_bitlen, eckey_can_do, eckey_check_pair +// +// - Const acccess / copies context to a stack based variable +// eckey_verify_wrap, eckey_sign_wrap: ../../../mbedtls-sys/vendor/crypto/library/pk_wrap.c:251 +// creates a stack ecdsa variable and uses ctx to initialize it. +// ctx is passed as 'key', a const pointer to mbedtls_ecdsa_from_keypair( &ecdsa, ctx ) +// ../../../mbedtls-sys/vendor/crypto/library/ecdsa.c:819 +// int mbedtls_ecdsa_from_keypair( mbedtls_ecdsa_context *ctx, const mbedtls_ecp_keypair *key ) +// key does not mutate. +// +// - Ignored due to not defined: eckey_verify_rs_wrap, eckey_sign_rs_wrap +// (Undefined - MBEDTLS_ECP_RESTARTABLE - ../../../mbedtls-sys/build/config.rs:173) +// +// - Only used when creating/freeing - which is safe by design - eckey_alloc_wrap / eckey_free_wrap +// +// 3. ECDSA: mbedtls_ecdsa_info at ../../../mbedtls-sys/vendor/crypto/library/pk_wrap.c:729 +// This does not use internal locks but avoids interior mutability. +// +// - Const access / copies context to stack based variables: +// ecdsa_verify_wrap: ../../../mbedtls-sys/vendor/crypto/library/pk_wrap.c:544 +// This copies the public key on the stack - in buf[] and copies the group id and nbits. +// That is done via: mbedtls_pk_write_pubkey( &p, buf, &key ) where key.pk_ctx = ctx; +// And the key is a const parameter to mbedtls_pk_write_pubkey - ../../../mbedtls-sys/vendor/crypto/library/pkwrite.c:158 +// +// - Const access with additional notes due to call stacks involved. +// +// ecdsa_sign_wrap: ../../../mbedtls-sys/vendor/crypto/library/pk_wrap.c:657 +// mbedtls_ecdsa_write_signature ../../../mbedtls-sys/vendor/crypto/library/ecdsa.c:688 +// mbedtls_ecdsa_write_signature_restartable ../../../mbedtls-sys/vendor/crypto/library/ecdsa.c:640 +// MBEDTLS_ECDSA_DETERMINISTIC is not defined. +// MBEDTLS_ECDSA_SIGN_ALT is not defined. +// Passes grp to: ecdsa_sign_restartable: ../../../mbedtls-sys/vendor/crypto/library/ecdsa.c:253 +// Const access to group - reads parameters, passed as const to mbedtls_ecp_gen_privkey, +// mbedtls_ecp_mul_restartable: ../../../mbedtls-sys/vendor/crypto/library/ecp.c:2351 +// MBEDTLS_ECP_INTERNAL_ALT is not defined. (otherwise it might not be safe depending on ecp_init/ecp_free) ../../../mbedtls-sys/build/config.rs:131 +// Passes as const to: mbedtls_ecp_check_privkey / mbedtls_ecp_check_pubkey / mbedtls_ecp_get_type( grp +// +// - Ignored due to not defined: ecdsa_verify_rs_wrap, ecdsa_sign_rs_wrap, ecdsa_rs_alloc, ecdsa_rs_free +// (Undefined - MBEDTLS_ECP_RESTARTABLE - ../../../mbedtls-sys/build/config.rs:173) +// +// - Only const access to context: eckey_check_pair +// +// - Only used when creating/freeing - which is safe by design: ecdsa_alloc_wrap, ecdsa_free_wrap +// +#[cfg(feature = "threading")] +unsafe impl Sync for Pk {} + impl Pk { /// Takes both DER and PEM forms of PKCS#1 or PKCS#8 encoded keys. /// @@ -778,7 +864,7 @@ impl Pk { sig: &mut [u8], rng: &mut F, ) -> Result { - use crate::rng::RngCallback; + use crate::rng::RngCallbackMut; if self.pk_type() == Type::Ecdsa || self.pk_type() == Type::Eckey { if sig.len() < ECDSA_MAX_LEN { @@ -803,8 +889,8 @@ impl Pk { hash.len(), sig.as_mut_ptr(), &mut ret, - Some(Rfc6979Rng::call), - rng.data_ptr(), + Some(Rfc6979Rng::call_mut), + rng.data_ptr_mut(), ).into_result()?; }; Ok(ret) diff --git a/mbedtls/src/pk/rfc6979.rs b/mbedtls/src/pk/rfc6979.rs index 1bc42f134..30da45d62 100644 --- a/mbedtls/src/pk/rfc6979.rs +++ b/mbedtls/src/pk/rfc6979.rs @@ -12,7 +12,7 @@ use crate::alloc_prelude::*; use mbedtls_sys::types::raw_types::{c_int, c_uchar, c_void}; use mbedtls_sys::types::size_t; -use crate::rng::{HmacDrbg, Random, RngCallback}; +use crate::rng::{HmacDrbg, Random, RngCallbackMut}; use crate::error::Result; use crate::bignum::Mpi; @@ -67,7 +67,7 @@ fn generate_rfc6979_nonce(md: &MdInfo, x: &Mpi, q: &Mpi, digest_bytes: &[u8]) -> pub(crate) struct Rfc6979Rng { pub k: Vec, pub k_read: usize, - pub rng: HmacDrbg<'static>, + pub rng: HmacDrbg, } /// An RNG which first outputs the k for RFC 6797 followed by random data @@ -110,8 +110,8 @@ impl Rfc6979Rng { } } -impl RngCallback for Rfc6979Rng { - unsafe extern "C" fn call( +impl RngCallbackMut for Rfc6979Rng { + unsafe extern "C" fn call_mut( user_data: *mut c_void, data_ptr: *mut c_uchar, len: size_t, @@ -126,7 +126,7 @@ impl RngCallback for Rfc6979Rng { } } - fn data_ptr(&mut self) -> *mut c_void { + fn data_ptr_mut(&mut self) -> *mut c_void { self as *const _ as *mut _ } } diff --git a/mbedtls/src/pkcs12/mod.rs b/mbedtls/src/pkcs12/mod.rs index 7d7507996..a70e98cfd 100644 --- a/mbedtls/src/pkcs12/mod.rs +++ b/mbedtls/src/pkcs12/mod.rs @@ -39,6 +39,7 @@ use crate::cipher::{Cipher, Decryption, Fresh, Traditional}; use crate::hash::{pbkdf_pkcs12, Md, MdInfo, Type as MdType}; use crate::pk::Pk; use crate::x509::Certificate; +use crate::alloc::{Box as MbedtlsBox}; use crate::Error as MbedtlsError; // Constants for various object identifiers used in PKCS12: @@ -836,7 +837,7 @@ impl Pfx { /// of "friendly names" which are associated with said certificate. /// Some or all of the certificates stored in a Pfx may be encrypted in which case /// decrypt must be called to access them. - pub fn certificates<'a>(&'a self) -> impl Iterator, Vec)> + 'a { + pub fn certificates<'a>(&'a self) -> impl Iterator, crate::Error>, Vec)> + 'a { self.authsafe_decrypted_contents() .filter_map(|sb| if let Pkcs12BagSet::Cert(CertBag(Some(cert))) = &sb.bag_value { Some((Certificate::from_der(cert), sb.friendly_name())) diff --git a/mbedtls/src/rng/ctr_drbg.rs b/mbedtls/src/rng/ctr_drbg.rs index 1b4184ba2..444a9ec25 100644 --- a/mbedtls/src/rng/ctr_drbg.rs +++ b/mbedtls/src/rng/ctr_drbg.rs @@ -6,126 +6,77 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ +use mbedtls_sys::*; +use std::sync::Arc; use mbedtls_sys::types::raw_types::{c_int, c_uchar, c_void}; use mbedtls_sys::types::size_t; pub use mbedtls_sys::CTR_DRBG_RESEED_INTERVAL as RESEED_INTERVAL; -use mbedtls_sys::{ - ctr_drbg_random, ctr_drbg_reseed, ctr_drbg_seed, ctr_drbg_set_prediction_resistance, - ctr_drbg_update, CTR_DRBG_PR_OFF, CTR_DRBG_PR_ON, -}; - -use super::{EntropyCallback, RngCallback}; use crate::error::{IntoResult, Result}; +use crate::rng::{EntropyCallback, RngCallback, RngCallbackMut}; + +define!( + // Moving data causes dangling pointers: https://github.com/ARMmbed/mbedtls/issues/2147 + // Storing data in heap and forcing rust move to only move the pointer (box) referencing it. + // The move will be faster. Access to data will be slower due to additional indirection. + #[c_box_ty(ctr_drbg_context)] + #[repr(C)] + struct CtrDrbg { + entropy: Arc, + }; + const drop: fn(&mut Self) = ctr_drbg_free; +); + +/// +/// Class has interior mutability via function called 'call'. +/// That function has an internal mutex to guarantee thread safety. +/// +/// The other potential conflict is a mutable reference changing class. +/// That is avoided by having any users of the callback hold an 'Arc' to this class. +/// Rust will then ensure that a mutable reference cannot be aquired if more then 1 Arc exists to the same class. +/// +#[cfg(feature = "threading")] +unsafe impl Sync for CtrDrbg {} -// ==== BEGIN IMMOVABLE TYPE KLUDGE ==== -// `ctr_drbg_context` inlines an `aes_context`, which is immovable. See -// https://github.com/ARMmbed/mbedtls/issues/2147. We work around this -// by always boxing up the context, which requires this module to depend on -// std/alloc. -// -// If `ctr_drbg_context` were moveable, this entire section could be replaced -// by basically: -// ``` -// define!( -// #[c_ty(ctr_drbg_context)] -// struct CtrDrbg<'entropy>; -// fn init() { -// ctr_drbg_init -// } -// fn drop() { -// ctr_drbg_free -// } -// ); -// ``` - -use self::private::CtrDrbgInner; -#[cfg(not(feature = "std"))] -use crate::alloc_prelude::*; -use core::ops::{Deref, DerefMut}; - -mod private { - use core::marker::PhantomData; - use mbedtls_sys::{ctr_drbg_context, ctr_drbg_free, ctr_drbg_init}; - - pub struct CtrDrbgInner<'entropy> { - pub(super) inner: ctr_drbg_context, - r: PhantomData<&'entropy ()>, - } - - impl<'entropy> CtrDrbgInner<'entropy> { - pub(super) fn init() -> Self { - let mut inner = ::core::mem::MaybeUninit::uninit(); - let inner = unsafe { - ctr_drbg_init(inner.as_mut_ptr()); - inner.assume_init() - }; - CtrDrbgInner { - inner, - r: PhantomData, - } - } - } - - impl<'entropy> Drop for CtrDrbgInner<'entropy> { - fn drop(&mut self) { - unsafe { ctr_drbg_free(&mut self.inner) }; - } - } -} +#[allow(dead_code)] +impl CtrDrbg { -pub struct CtrDrbg<'entropy> { - boxed: Box>, -} + // If threading enabled - we are Sync, but for that to be true we need entropy to also be sync. + #[cfg(feature = "threading")] + pub fn new(entropy: Arc, additional_entropy: Option<&[u8]>) -> Result { + let mut inner = Box::new(ctr_drbg_context::default()); -impl<'entropy> CtrDrbg<'entropy> { - fn init() -> Self { - CtrDrbg { - boxed: Box::new(CtrDrbgInner::init()), + unsafe { + ctr_drbg_init(&mut *inner); + ctr_drbg_seed( + &mut *inner, + Some(T::call), + entropy.data_ptr(), + additional_entropy.map(<[_]>::as_ptr).unwrap_or(::core::ptr::null()), + additional_entropy.map(<[_]>::len).unwrap_or(0) + ).into_result()?; } - } -} -#[doc(hidden)] -impl<'entropy> Deref for CtrDrbg<'entropy> { - type Target = CtrDrbgInner<'entropy>; - fn deref(&self) -> &Self::Target { - &self.boxed + Ok(CtrDrbg { inner, entropy }) } -} -#[doc(hidden)] -impl<'entropy> DerefMut for CtrDrbg<'entropy> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.boxed - } -} - -// ==== END IMMOVABLE TYPE KLUDGE ==== + #[cfg(not(feature = "threading"))] + pub fn new(entropy: Arc, additional_entropy: Option<&[u8]>) -> Result { + let mut inner = Box::new(ctr_drbg_context::default()); -#[cfg(feature = "threading")] -unsafe impl<'entropy> Sync for CtrDrbg<'entropy> {} - -impl<'entropy> CtrDrbg<'entropy> { - pub fn new( - source: &'entropy mut F, - additional_entropy: Option<&[u8]>, - ) -> Result> { - let mut ret = Self::init(); unsafe { + ctr_drbg_init(&mut *inner); ctr_drbg_seed( - &mut ret.inner, - Some(F::call), - source.data_ptr(), - additional_entropy - .map(<[_]>::as_ptr) - .unwrap_or(::core::ptr::null()), + &mut *inner, + Some(T::call), + entropy.data_ptr(), + additional_entropy.map(<[_]>::as_ptr).unwrap_or(::core::ptr::null()), additional_entropy.map(<[_]>::len).unwrap_or(0) - ) - .into_result()? - }; - Ok(ret) - } + ).into_result()?; + } + Ok(CtrDrbg { inner, entropy }) + } + pub fn prediction_resistance(&self) -> bool { if self.inner.prediction_resistance == CTR_DRBG_PR_OFF { false @@ -137,21 +88,32 @@ impl<'entropy> CtrDrbg<'entropy> { pub fn set_prediction_resistance(&mut self, pr: bool) { unsafe { ctr_drbg_set_prediction_resistance( - &mut self.inner, + &mut *self.inner, if pr { CTR_DRBG_PR_ON } else { CTR_DRBG_PR_OFF }, ) } } - getter!(entropy_len() -> size_t = .entropy_len); - setter!(set_entropy_len(len: size_t) = ctr_drbg_set_entropy_len); - getter!(reseed_interval() -> c_int = .reseed_interval); - setter!(set_reseed_interval(i: c_int) = ctr_drbg_set_reseed_interval); + pub fn entropy_len(&self) -> size_t { + self.inner.entropy_len + } + + pub fn set_entropy_len(&mut self, len: size_t) { + unsafe { ctr_drbg_set_entropy_len(&mut *self.inner, len); } + } + + pub fn reseed_interval(&self) -> c_int { + self.inner.reseed_interval + } + + pub fn set_reseed_interval(&mut self, i: c_int) { + unsafe { ctr_drbg_set_reseed_interval(&mut *self.inner, i); } + } pub fn reseed(&mut self, additional_entropy: Option<&[u8]>) -> Result<()> { unsafe { ctr_drbg_reseed( - &mut self.inner, + &mut *self.inner, additional_entropy .map(<[_]>::as_ptr) .unwrap_or(::core::ptr::null()), @@ -163,24 +125,30 @@ impl<'entropy> CtrDrbg<'entropy> { } pub fn update(&mut self, entropy: &[u8]) { - unsafe { ctr_drbg_update(&mut self.inner, entropy.as_ptr(), entropy.len()) }; + unsafe { ctr_drbg_update(&mut *self.inner, entropy.as_ptr(), entropy.len()) }; } - - // TODO: - // - // ctr_drbg_random_with_add - // ctr_drbg_write_seed_file - // ctr_drbg_update_seed_file - // } -impl<'entropy> RngCallback for CtrDrbg<'entropy> { +impl RngCallbackMut for CtrDrbg { #[inline(always)] - unsafe extern "C" fn call(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { + unsafe extern "C" fn call_mut(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int where Self: std::marker::Sized { + // Mutex used in ctr_drbg_random at: ../../../mbedtls-sys/vendor/crypto/library/ctr_drbg.c:546 ctr_drbg_random(user_data, data, len) } - fn data_ptr(&mut self) -> *mut c_void { - &mut self.inner as *mut _ as *mut _ + fn data_ptr_mut(&mut self) -> *mut c_void { + self.handle_mut() as *const _ as *mut _ + } +} + +impl RngCallback for CtrDrbg { + #[inline(always)] + unsafe extern "C" fn call(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int where Self: std::marker::Sized { + // Mutex used in ctr_drbg_random at: ../../../mbedtls-sys/vendor/crypto/library/ctr_drbg.c:546 + ctr_drbg_random(user_data, data, len) + } + + fn data_ptr(&self) -> *mut c_void { + self.handle() as *const _ as *mut _ } } diff --git a/mbedtls/src/rng/hmac_drbg.rs b/mbedtls/src/rng/hmac_drbg.rs index 5320dd672..76263573d 100644 --- a/mbedtls/src/rng/hmac_drbg.rs +++ b/mbedtls/src/rng/hmac_drbg.rs @@ -9,41 +9,44 @@ use mbedtls_sys::types::raw_types::{c_int, c_uchar, c_void}; use mbedtls_sys::types::size_t; pub use mbedtls_sys::HMAC_DRBG_RESEED_INTERVAL as RESEED_INTERVAL; -use mbedtls_sys::{ - hmac_drbg_random, hmac_drbg_reseed, hmac_drbg_seed, hmac_drbg_seed_buf, - hmac_drbg_set_prediction_resistance, hmac_drbg_update, HMAC_DRBG_PR_OFF, HMAC_DRBG_PR_ON, -}; +use mbedtls_sys::*; -use super::{EntropyCallback, RngCallback}; +use crate::rng::{EntropyCallback, RngCallback, RngCallbackMut}; use crate::error::{IntoResult, Result}; use crate::hash::MdInfo; +use std::sync::Arc; define!( #[c_ty(hmac_drbg_context)] - struct HmacDrbg<'entropy>; - const init: fn() -> Self = hmac_drbg_init; + struct HmacDrbg { + entropy: Option>, + }; const drop: fn(&mut Self) = hmac_drbg_free; ); #[cfg(feature = "threading")] -unsafe impl<'entropy> Sync for HmacDrbg<'entropy> {} +unsafe impl Sync for HmacDrbg {} -impl<'entropy> HmacDrbg<'entropy> { - pub fn new( +impl HmacDrbg { + pub fn new( md_info: MdInfo, - source: &'entropy mut F, + entropy: Arc, additional_entropy: Option<&[u8]>, - ) -> Result> { - let mut ret = Self::init(); + ) -> Result { + + let mut ret = HmacDrbg { + inner: hmac_drbg_context::default(), + entropy: Some(entropy), + }; + unsafe { + hmac_drbg_init(&mut ret.inner); hmac_drbg_seed( &mut ret.inner, md_info.into(), - Some(F::call), - source.data_ptr(), - additional_entropy - .map(<[_]>::as_ptr) - .unwrap_or(::core::ptr::null()), + Some(T::call), + ret.entropy.as_ref().unwrap().data_ptr(), + additional_entropy.map(<[_]>::as_ptr).unwrap_or(::core::ptr::null()), additional_entropy.map(<[_]>::len).unwrap_or(0) ) .into_result()? @@ -51,9 +54,15 @@ impl<'entropy> HmacDrbg<'entropy> { Ok(ret) } - pub fn from_buf(md_info: MdInfo, entropy: &[u8]) -> Result> { - let mut ret = Self::init(); + + pub fn from_buf(md_info: MdInfo, entropy: &[u8]) -> Result { + let mut ret = HmacDrbg { + inner: hmac_drbg_context::default(), + entropy: None, + }; + unsafe { + hmac_drbg_init(&mut ret.inner); hmac_drbg_seed_buf( &mut ret.inner, md_info.into(), @@ -117,13 +126,25 @@ impl<'entropy> HmacDrbg<'entropy> { // } -impl<'entropy> RngCallback for HmacDrbg<'entropy> { +impl RngCallbackMut for HmacDrbg { + #[inline(always)] + unsafe extern "C" fn call_mut(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { + hmac_drbg_random(user_data, data, len) + } + + fn data_ptr_mut(&mut self) -> *mut c_void { + self.handle_mut() as *const _ as *mut _ + } +} + +impl RngCallback for HmacDrbg { #[inline(always)] unsafe extern "C" fn call(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { + // Mutex used in hmac_drbg_random: ../../../mbedtls-sys/vendor/crypto/library/hmac_drbg.c:363 hmac_drbg_random(user_data, data, len) } - fn data_ptr(&mut self) -> *mut c_void { - &mut self.inner as *mut _ as *mut _ + fn data_ptr(&self) -> *mut c_void { + self.handle() as *const _ as *mut _ } } diff --git a/mbedtls/src/rng/mod.rs b/mbedtls/src/rng/mod.rs index 364ac3b7a..99ff169d1 100644 --- a/mbedtls/src/rng/mod.rs +++ b/mbedtls/src/rng/mod.rs @@ -27,11 +27,11 @@ use crate::error::{Result, IntoResult}; use mbedtls_sys::types::raw_types::{c_int, c_uchar}; use mbedtls_sys::types::size_t; -callback!(EntropyCallback:Sync(data: *mut c_uchar, len: size_t) -> c_int); -callback!(RngCallback:Sync(data: *mut c_uchar, len: size_t) -> c_int); +callback!(EntropyCallbackMut,EntropyCallback:Sync(data: *mut c_uchar, len: size_t) -> c_int); +callback!(RngCallbackMut,RngCallback:Sync(data: *mut c_uchar, len: size_t) -> c_int); pub trait Random: RngCallback { - fn random(&mut self, data: &mut [u8]) -> Result<()> { + fn random(&mut self, data: &mut [u8]) -> Result<()> where Self: std::marker::Sized { unsafe { Self::call(self.data_ptr(), data.as_mut_ptr(), data.len()) }.into_result()?; Ok(()) } diff --git a/mbedtls/src/rng/os_entropy.rs b/mbedtls/src/rng/os_entropy.rs index ec1e333d4..2593a2911 100644 --- a/mbedtls/src/rng/os_entropy.rs +++ b/mbedtls/src/rng/os_entropy.rs @@ -6,71 +6,107 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ +use crate::error::{IntoResult, Result}; +use crate::rng::EntropyCallback; +use mbedtls_sys::*; use mbedtls_sys::types::raw_types::{c_int, c_uchar, c_void}; use mbedtls_sys::types::size_t; -use mbedtls_sys::*; +use std::sync::Arc; -use crate::error::{IntoResult, Result}; - -callback!(EntropySourceCallback(data: *mut c_uchar, size: size_t, out: *mut size_t) -> c_int); +callback!(EntropySourceCallbackMut,EntropySourceCallback(data: *mut c_uchar, size: size_t, out: *mut size_t) -> c_int); define!( - #[c_ty(entropy_context)] - struct OsEntropy<'source>; - pub const new: fn() -> Self = entropy_init; + // Moving data causes dangling pointers: https://github.com/ARMmbed/mbedtls/issues/2147 + // Storing data in heap and forcing rust move to only move the pointer (box) referencing it. + // The move will be faster. Access to data will be slower due to additional indirection. + #[c_box_ty(entropy_context)] + #[repr(C)] + struct OsEntropy { + sources: Vec>, + }; + impl<'a> Into {} const drop: fn(&mut Self) = entropy_free; ); +/// +/// Class has interior mutability via function called 'call'. +/// That function has an internal mutex to guarantee thread safety. +/// +/// The other potential conflict is a mutable reference changing class. +/// That is avoided by having any users of the callback hold an 'Arc' to this class. +/// Rust will then ensure that a mutable reference cannot be aquired if more then 1 Arc exists to the same class. +/// #[cfg(feature = "threading")] -unsafe impl<'source> Sync for OsEntropy<'source> {} +unsafe impl Sync for OsEntropy {} + +#[allow(dead_code)] +impl OsEntropy { + + pub fn new() -> Self { + let mut inner = Box::new(entropy_context::default()); -impl<'source> OsEntropy<'source> { - pub fn add_source( + unsafe { + entropy_init(&mut *inner); + }; + + OsEntropy { + inner, + sources: vec![], + } + } + + pub fn add_source( &mut self, - source: &'source mut F, + source: Arc, threshold: size_t, strong: bool, ) -> Result<()> { unsafe { + // add_source is guarded with internal mutex: mbedtls-sys/vendor/crypto/library/entropy.c:143 + // all sources are called at later points via 'entropy_gather_internal' which in turn is called with internal mutex locked. entropy_add_source( - &mut self.inner, + self.into(), Some(F::call), source.data_ptr(), threshold, - if strong { - ENTROPY_SOURCE_STRONG - } else { - ENTROPY_SOURCE_WEAK - } + if strong { ENTROPY_SOURCE_STRONG } else { ENTROPY_SOURCE_WEAK } ) .into_result()? }; + + // Rust ensures only one mutable reference is currently in use. + self.sources.push(source); Ok(()) } pub fn gather(&mut self) -> Result<()> { - unsafe { entropy_gather(&mut self.inner) }.into_result()?; + // function is guarded with internal mutex: mbedtls-sys/vendor/crypto/library/entropy.c:310 + unsafe { entropy_gather(self.into()) }.into_result()?; Ok(()) } pub fn update_manual(&mut self, data: &[u8]) -> Result<()> { - unsafe { entropy_update_manual(&mut self.inner, data.as_ptr(), data.len()) }.into_result()?; + // function is guarded with internal mutex: mbedtls-sys/vendor/crypto/library/entropy.c:241 + unsafe { entropy_update_manual(self.into(), data.as_ptr(), data.len()) }.into_result()?; Ok(()) } + // TODO // entropy_write_seed_file // entropy_update_seed_file // } -impl<'source> super::EntropyCallback for OsEntropy<'source> { +impl EntropyCallback for OsEntropy { #[inline(always)] unsafe extern "C" fn call(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { + // mutex used in entropy_func: ../../../mbedtls-sys/vendor/crypto/library/entropy.c:348 + // note: we're not using MBEDTLS_ENTROPY_NV_SEED so the initialization is not present or a race condition. entropy_func(user_data, data, len) } - fn data_ptr(&mut self) -> *mut c_void { - &mut self.inner as *mut _ as *mut _ + fn data_ptr(&self) -> *mut c_void { + &*self.inner as *const _ as *mut _ } } diff --git a/mbedtls/src/rng/rdrand.rs b/mbedtls/src/rng/rdrand.rs index 9b734a6b7..6ca4936f1 100644 --- a/mbedtls/src/rng/rdrand.rs +++ b/mbedtls/src/rng/rdrand.rs @@ -64,7 +64,7 @@ fn write_rng_to_slice(outbuf: &mut [u8], rng: fn() -> Option) -> c_int { 0 } -use super::{EntropyCallback, RngCallback}; +use super::{EntropyCallback, EntropyCallbackMut, RngCallback, RngCallbackMut}; pub struct Entropy; @@ -74,20 +74,49 @@ impl EntropyCallback for Entropy { write_rng_to_slice(&mut outbuf, rdseed) } - fn data_ptr(&mut self) -> *mut c_void { + fn data_ptr(&self) -> *mut c_void { + ::core::ptr::null_mut() + } +} + +impl EntropyCallbackMut for Entropy { + unsafe extern "C" fn call_mut(_: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { + let mut outbuf = from_raw_parts_mut(data, len); + write_rng_to_slice(&mut outbuf, rdseed) + } + + fn data_ptr_mut(&mut self) -> *mut c_void { ::core::ptr::null_mut() } } pub struct Nrbg; +impl RngCallbackMut for Nrbg { + unsafe extern "C" fn call_mut(_: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { + // outbuf data/len are stack variables + let mut outbuf = from_raw_parts_mut(data, len); + + // rdrand function is thread safe + write_rng_to_slice(&mut outbuf, rdrand) + } + + fn data_ptr_mut(&mut self) -> *mut c_void { + ::core::ptr::null_mut() + } +} + impl RngCallback for Nrbg { unsafe extern "C" fn call(_: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { + // outbuf data/len are stack variables let mut outbuf = from_raw_parts_mut(data, len); + + // rdrand function is thread safe write_rng_to_slice(&mut outbuf, rdrand) } - fn data_ptr(&mut self) -> *mut c_void { + fn data_ptr(&self) -> *mut c_void { ::core::ptr::null_mut() } } + diff --git a/mbedtls/src/rust_malloc.c b/mbedtls/src/rust_malloc.c new file mode 100644 index 000000000..a7f051f7a --- /dev/null +++ b/mbedtls/src/rust_malloc.c @@ -0,0 +1,31 @@ +/* Copyright (c) Fortanix, Inc. + * + * Licensed under the GNU General Public License, version 2 or the Apache License, Version + * 2.0 , at your + * option. This file may not be copied, modified, or distributed except + * according to those terms. */ + +// Follow same pattern for config and alloc/free as everywhere in mbedtls +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PLATFORM_C) +#include "mbedtls/platform.h" +#else +#include +#define mbedtls_calloc calloc +#define mbedtls_free free +#endif + +extern void *forward_mbedtls_calloc( size_t n, size_t size ) { + return mbedtls_calloc(n, size); +} + +extern void forward_mbedtls_free( void *ptr ) { + mbedtls_free(ptr); +} + diff --git a/mbedtls/src/self_test.rs b/mbedtls/src/self_test.rs index 3a97b2244..940a92cea 100644 --- a/mbedtls/src/self_test.rs +++ b/mbedtls/src/self_test.rs @@ -44,20 +44,23 @@ pub unsafe extern "C" fn mbedtls_log(msg: *const c_char) { log_f.expect("Called self-test log without enabling self-test")(msg) } -// unsafe since unsynchronized +/// unsafe since unsynchronized #[cfg(any(target_os = "none", target_env = "sgx", not(feature = "std")))] pub unsafe fn enable(rand: fn() -> c_int, log: unsafe fn(*const c_char)) { rand_f = Some(rand); log_f = Some(log); } -// unsafe since unsynchronized +/// unsafe since unsynchronized #[cfg(any(target_os = "none", target_env = "sgx", not(feature = "std")))] pub unsafe fn disable() { rand_f = None; log_f = None; } +/// NOTE: The following functions if used in a multithreaded or async environment will fail. +/// Self test functions rely on global variables to track operations and anything non-self-test related will stomp over variables. +/// While using these, make sure no other code uses mbedtls. Multiple self test operations done asynchronously may also return failures. pub use mbedtls_sys::{ aes_self_test as aes, arc4_self_test as arc4, base64_self_test as base64, camellia_self_test as camellia, ccm_self_test as ccm, ctr_drbg_self_test as ctr_drbg, diff --git a/mbedtls/src/ssl/ciphersuites.rs b/mbedtls/src/ssl/ciphersuites.rs index 20cbeaac8..a0b39df03 100644 --- a/mbedtls/src/ssl/ciphersuites.rs +++ b/mbedtls/src/ssl/ciphersuites.rs @@ -9,6 +9,7 @@ use mbedtls_sys::types::raw_types::c_int; use mbedtls_sys::*; +/// Always use into() to convert to i32, do not use 'as i32'. (until issue is fixed: https://github.com/fortanix/rust-mbedtls/issues/129) define!( #[non_exhaustive] #[c_ty(c_int)] diff --git a/mbedtls/src/ssl/config.rs b/mbedtls/src/ssl/config.rs index 936d047db..a15f4d710 100644 --- a/mbedtls/src/ssl/config.rs +++ b/mbedtls/src/ssl/config.rs @@ -6,20 +6,23 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ -use core::slice::from_raw_parts; - -use mbedtls_sys::types::raw_types::{c_char, c_int, c_uchar, c_uint, c_void}; -use mbedtls_sys::types::size_t; -use mbedtls_sys::*; - -use crate::error::{Error, IntoResult, Result}; -use core::result::Result as StdResult; -use crate::pk::dhparam::Dhm; +use crate::error::{Error, Result, IntoResult}; use crate::pk::Pk; -use crate::private::UnsafeFrom; +use crate::rng::RngCallback; +use mbedtls_sys::*; +use mbedtls_sys::types::raw_types::{c_int, c_void, c_char, c_uchar, c_uint}; +use std::sync::Arc; +use crate::x509::Crl; +use crate::x509::Certificate; +use crate::x509::VerifyError; use crate::ssl::context::HandshakeContext; +use mbedtls_sys::types::size_t; +use std::slice::from_raw_parts; use crate::ssl::ticket::TicketCallback; -use crate::x509::{certificate, Crl, LinkedCertificate, Profile, VerifyError}; +use crate::pk::dhparam::Dhm; +use crate::x509::Profile; +use crate::private::UnsafeFrom; +use crate::alloc::{List as MbedtlsList}; extern "C" { fn calloc(n: usize, size: usize) -> *mut c_void; @@ -83,57 +86,147 @@ define!( } ); -callback!(DbgCallback:Sync(level: c_int, file: *const c_char, line: c_int, message: *const c_char) -> ()); +define!( + #[c_ty(c_int)] + enum Renegotiation { + Enabled = SSL_RENEGOTIATION_ENABLED, + Disabled = SSL_RENEGOTIATION_DISABLED, + } +); define!( - #[c_ty(ssl_config)] - struct Config<'c>; - const init: fn() -> Self = ssl_config_init; - const drop: fn(&mut Self) = ssl_config_free; - impl<'q> Into {} - impl<'q> UnsafeFrom {} + // Moving data may cause dangling pointers: https://github.com/ARMmbed/mbedtls/issues/2147 + // Storing data in heap and forcing rust move to only move the pointer (box) referencing it. + #[c_box_ty(ssl_config)] + #[repr(C)] + struct Config { + // Holding reference counters against any structures that ssl_config might hold pointer to. + // This allows caller to share structure on multiple configs if needed. + own_cert: Vec>>, + own_pk: Vec>, + + ca_cert: Option>>, + crl: Option>, + + // If we are sync we need all members to be sync, as such need to use cfg feature on all callbacks. + #[cfg(feature = "threading")] + rng: Option>, + #[cfg(not (feature = "threading"))] + rng: Option>, + + ciphersuites: Vec>>, + curves: Option>>, + + #[allow(dead_code)] + dhm: Option>, + + #[cfg(feature = "threading")] + verify_callback: Option Result<()>) + Send + Sync + 'static>>, + #[cfg(not (feature = "threading"))] + verify_callback: Option Result<()>) + 'static>>, + + #[cfg(feature = "threading")] + dbg_callback: Option ()) + Send + Sync + 'static>>, + #[cfg(not (feature = "threading"))] + dbg_callback: Option ()) + 'static>>, + + #[cfg(feature = "threading")] + sni_callback: Option Result<()>) + Send + Sync + 'static>>, + #[cfg(not (feature = "threading"))] + sni_callback: Option Result<()>) + 'static>>, + + #[cfg(feature = "threading")] + ticket_callback: Option>, + #[cfg(not (feature = "threading"))] + ticket_callback: Option>, + + #[cfg(feature = "threading")] + ca_callback: Option Result<()>) + Send + Sync + 'static>>, + #[cfg(not (feature = "threading"))] + ca_callback: Option Result<()>) + 'static>>, + }; + impl<'a> Into {} ); #[cfg(feature = "threading")] -unsafe impl<'c> Sync for Config<'c> {} +unsafe impl Sync for Config {} -impl<'c> Config<'c> { +impl Config { pub fn new(e: Endpoint, t: Transport, p: Preset) -> Self { - let mut c = Config::init(); + let mut inner = Box::new(ssl_config::default()); + unsafe { - ssl_config_defaults(&mut c.inner, e.into(), t.into(), p.into()); + // This is just a memset to 0. + ssl_config_init(&mut *inner); + + // Set default values - after this point we will need ssl_config_free to be called. + ssl_config_defaults(&mut *inner, e as c_int, t as c_int, p as c_int); + }; + + Config { + inner, + own_cert: vec![], + own_pk: vec![], + ca_cert: None, + crl: None, + rng: None, + ciphersuites: vec![], + curves: None, + dhm: None, + verify_callback: None, + dbg_callback: None, + sni_callback: None, + ticket_callback: None, + ca_callback: None, } - c } - // need bitfield support getter!(endpoint() -> Endpoint = field endpoint); - setter!(set_endpoint(e: Endpoint) = ssl_conf_endpoint); - // need bitfield support getter!(transport() -> Transport = field transport); - setter!(set_transport(t: Transport) = ssl_conf_transport); - // need bitfield support getter!(authmode() -> AuthMode = field authmode); - setter!(set_authmode(am: AuthMode) = ssl_conf_authmode); - getter!(read_timeout() -> u32 = .read_timeout); - setter!(set_read_timeout(t: u32) = ssl_conf_read_timeout); + pub fn set_authmode(&mut self, authmode: AuthMode) { + unsafe { ssl_conf_authmode(self.into(), authmode as c_int); } + } + + pub fn read_timeout(&self) -> u32 { + self.inner.read_timeout + } + + pub fn set_read_timeout(&mut self, t: u32) { + unsafe { ssl_conf_read_timeout(self.into(), t); } + } fn check_c_list(list: &[T]) { assert!(list.last() == Some(&T::default())); } - pub fn set_ciphersuites(&mut self, list: &'c [c_int]) { - Self::check_c_list(list); - unsafe { ssl_conf_ciphersuites(&mut self.inner, list.as_ptr()) } + pub fn set_ciphersuites(&mut self, list: Arc>) { + Self::check_c_list(&list); + + unsafe { ssl_conf_ciphersuites(self.into(), list.as_ptr()) } + self.ciphersuites.push(list); } - pub fn set_ciphersuites_for_version(&mut self, list: &'c [c_int], major: c_int, minor: c_int) { - Self::check_c_list(list); - unsafe { ssl_conf_ciphersuites_for_version(&mut self.inner, list.as_ptr(), major, minor) } + pub fn set_ciphersuites_for_version(&mut self, list: Arc>, major: c_int, minor: c_int) { + Self::check_c_list(&list); + unsafe { ssl_conf_ciphersuites_for_version(self.into(), list.as_ptr(), major, minor) } + self.ciphersuites.push(list); } - pub fn set_curves(&mut self, list: &'c [ecp_group_id]) { - Self::check_c_list(list); - unsafe { ssl_conf_curves(&mut self.inner, list.as_ptr()) } + pub fn set_curves(&mut self, list: Arc>) { + Self::check_c_list(&list); + unsafe { ssl_conf_curves(self.into(), list.as_ptr()) } + self.curves = Some(list); } + #[cfg(feature = "threading")] + pub fn set_rng(&mut self, rng: Arc) { + unsafe { ssl_conf_rng(self.into(), Some(T::call), rng.data_ptr()) }; + self.rng = Some(rng); + } + #[cfg(not (feature = "threading"))] + pub fn set_rng(&mut self, rng: Arc) { + unsafe { ssl_conf_rng(self.into(), Some(T::call), rng.data_ptr()) }; + self.rng = Some(rng); + } + pub fn set_min_version(&mut self, version: Version) -> Result<()> { let minor = match version { Version::Ssl3 => 0, @@ -143,7 +236,7 @@ impl<'c> Config<'c> { _ => { return Err(Error::SslBadHsProtocolVersion); } }; - unsafe { ssl_conf_min_version(&mut self.inner, 3, minor) }; + unsafe { ssl_conf_min_version(self.into(), 3, minor) }; Ok(()) } @@ -155,186 +248,165 @@ impl<'c> Config<'c> { Version::Tls1_2 => 3, _ => { return Err(Error::SslBadHsProtocolVersion); } }; - unsafe { ssl_conf_max_version(&mut self.inner, 3, minor) }; + unsafe { ssl_conf_max_version(self.into(), 3, minor) }; Ok(()) } - setter!(set_cert_profile(p: &'c Profile) = ssl_conf_cert_profile); + // Profile as implemented in profile.rs can only point to global variables from mbedtls which would have 'static lifetime + pub fn set_cert_profile(&mut self, p: &'static Profile) { + unsafe { ssl_conf_cert_profile(self.into(), p.into()) }; + } /// Takes both DER and PEM forms of FFDH parameters in `DHParams` format. /// /// When calling on PEM-encoded data, `params` must be NULL-terminated pub fn set_dh_params(&mut self, params: &[u8]) -> Result<()> { - let mut ctx = Dhm::from_params(params)?; + let dhm = Arc::new(Dhm::from_params(params)?); unsafe { - ssl_conf_dh_param_ctx(&mut self.inner, (&mut ctx).into()) + ssl_conf_dh_param_ctx(self.into(), dhm.inner_ffi_mut()) .into_result() - .map(|_| ()) + .map(|_| ())?; } + self.dhm = Some(dhm); + Ok(()) } - pub fn set_ca_list>( - &mut self, - list: Option, - crl: Option<&'c mut Crl>, - ) { - unsafe { - ssl_conf_ca_chain( - &mut self.inner, - list.map(Into::into) - .map(Into::into) - .unwrap_or(::core::ptr::null_mut()), - crl.map(Into::into).unwrap_or(::core::ptr::null_mut()), - ) - } - } + pub fn set_ca_list(&mut self, ca_cert: Arc>, crl: Option>) { + // This will override internal pointers to what we provide. + + unsafe { ssl_conf_ca_chain(self.into(), ca_cert.inner_ffi_mut(), crl.as_ref().map(|crl| crl.inner_ffi_mut()).unwrap_or(::core::ptr::null_mut())); } - pub fn push_cert>( - &mut self, - chain: C, - key: &'c mut Pk, - ) -> Result<()> { - unsafe { - ssl_conf_own_cert(&mut self.inner, chain.into().into(), key.into()) - .into_result() - .map(|_| ()) - } + self.ca_cert = Some(ca_cert); + self.crl = crl; } - pub fn certs(&'c self) -> KeyCertIter<'c> { - KeyCertIter { - key_cert: unsafe { UnsafeFrom::from(self.inner.key_cert as *const _) }, + pub fn push_cert(&mut self, own_cert: Arc>, own_pk: Arc) -> Result<()> { + // Need to ensure own_cert/pk_key outlive the config. + self.own_cert.push(own_cert.clone()); + self.own_pk.push(own_pk.clone()); + + // This will append pointers to our certificates inside mbedtls + unsafe { ssl_conf_own_cert(self.into(), own_cert.inner_ffi_mut(), own_pk.inner_ffi_mut()) + .into_result() + .map(|_| ()) } } - + /// Server only: configure callback to use for generating/interpreting session tickets. - pub fn set_session_tickets_callback(&mut self, cb: &'c mut F) { + pub fn set_session_tickets_callback(&mut self, cb: Arc) { unsafe { ssl_conf_session_tickets_cb( - &mut self.inner, - Some(F::call_write), - Some(F::call_parse), + self.into(), + Some(T::call_write), + Some(T::call_parse), cb.data_ptr(), ) }; + + self.ticket_callback = Some(cb); } - setter!( - /// Client only: whether to remember and use session tickets - set_session_tickets(u: UseSessionTickets) = ssl_conf_session_tickets - ); - - setter!( - /// Client only: minimal FFDH group size - set_ffdh_min_bitlen(bitlen: c_uint) = ssl_conf_dhm_min_bitlen - ); - - // TODO: The lifetime restrictions on HandshakeContext here are too strict. - // Once we need something else, we might fix it. - pub fn set_sni_callback StdResult<(), ()>>( - &mut self, - cb: &'c mut F, - ) { - unsafe extern "C" fn sni_callback< - F: FnMut(&mut HandshakeContext, &[u8]) -> StdResult<(), ()>, - >( - closure: *mut c_void, - ctx: *mut ssl_context, - name: *const c_uchar, - name_len: size_t, - ) -> c_int { - let cb = &mut *(closure as *mut F); - let mut ctx = UnsafeFrom::from(ctx).expect("valid context"); - let name = from_raw_parts(name, name_len); - match cb(&mut ctx, name) { - Ok(()) => 0, - Err(()) => -1, - } - } + pub fn set_session_tickets(&mut self, u: UseSessionTickets) { + unsafe { ssl_conf_session_tickets(self.into(), u.into()); } + } + + pub fn set_renegotiation(&mut self, u: Renegotiation) { + unsafe { ssl_conf_renegotiation(self.into(), u.into()); } + } + + /// Client only: minimal FFDH group size + pub fn set_ffdh_min_bitlen(&mut self, bitlen: c_uint) { + unsafe { ssl_conf_dhm_min_bitlen(self.into(), bitlen); } + } + + #[cfg(feature = "threading")] + pub fn set_sni_callback(&mut self, cb: F) + where + F: Fn(&mut HandshakeContext, &[u8]) -> Result<()> + Send + Sync + 'static, + { + self.sni_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_sni(self.into(), Some(sni_callback::), &**self.sni_callback.as_mut().unwrap() as *const _ as *mut c_void) } + } - unsafe { ssl_conf_sni(&mut self.inner, Some(sni_callback::), cb as *mut F as _) } + #[cfg(not(feature = "threading"))] + pub fn set_sni_callback(&mut self, cb: F) + where + F: Fn(&mut HandshakeContext, &[u8]) -> Result<()> + 'static, + { + self.sni_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_sni(self.into(), Some(sni_callback::), &**self.sni_callback.as_mut().unwrap() as *const _ as *mut c_void) } } // The docs for mbedtls_x509_crt_verify say "The [callback] should return 0 for anything but a // fatal error.", so verify callbacks should return Ok(()) for anything but a fatal error. // Report verification errors by updating the flags in VerifyError. - pub fn set_verify_callback(&mut self, cb: &'c mut F) + #[cfg(feature = "threading")] + pub fn set_verify_callback(&mut self, cb: F) where - F: FnMut(&mut LinkedCertificate, i32, &mut VerifyError) -> Result<()>, + F: Fn(&Certificate, i32, &mut VerifyError) -> Result<()> + Send + Sync + 'static, { - unsafe extern "C" fn verify_callback( - closure: *mut c_void, - crt: *mut x509_crt, - depth: c_int, - flags: *mut u32, - ) -> c_int - where - F: FnMut(&mut LinkedCertificate, i32, &mut VerifyError) -> Result<()>, - { - let cb = &mut *(closure as *mut F); - let crt: &mut LinkedCertificate = - UnsafeFrom::from(crt).expect("valid certificate"); - let mut verify_error = match VerifyError::from_bits(*flags) { - Some(ve) => ve, - // This can only happen if mbedtls is setting flags in VerifyError that are - // missing from our definition. - None => return ::mbedtls_sys::ERR_X509_BAD_INPUT_DATA, - }; - let res = cb(crt, depth, &mut verify_error); - *flags = verify_error.bits(); - match res { - Ok(()) => 0, - Err(e) => e.to_int(), - } - } + self.verify_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_verify(self.into(), Some(verify_callback::), &**self.verify_callback.as_mut().unwrap() as *const _ as *mut c_void) } + } - unsafe { - ssl_conf_verify( - &mut self.inner, - Some(verify_callback::), - cb as *mut F as _, - ) - } + #[cfg(not(feature = "threading"))] + pub fn set_verify_callback(&mut self, cb: F) + where + F: Fn(&Certificate, i32, &mut VerifyError) -> Result<()> + 'static, + { + self.verify_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_verify(self.into(), Some(verify_callback::), &**self.verify_callback.as_ref().unwrap() as *const _ as *mut c_void) } } - pub fn set_ca_callback(&mut self, cb: &'c mut F) - where - F: FnMut(&LinkedCertificate, &mut ForeignOwnedCertListBuilder) -> Result<()>, + #[cfg(feature = "threading")] + pub fn set_ca_callback(&mut self, cb: F) + where + F: Fn(&Certificate, &mut ForeignOwnedCertListBuilder) -> Result<()> + Send + Sync + 'static, { - unsafe extern "C" fn ca_callback( - closure: *mut c_void, - child: *const x509_crt, - candidate_cas: *mut *mut x509_crt - ) -> c_int - where - F: FnMut(&LinkedCertificate, &mut ForeignOwnedCertListBuilder) -> Result<()>, - { - let cb = &mut *(closure as *mut F); - let child: &LinkedCertificate = UnsafeFrom::from(child).expect("valid child certificate"); - let mut cert_builder = ForeignOwnedCertListBuilder::new(); - match cb(child, &mut cert_builder) { - Ok(()) => { - *candidate_cas = cert_builder.to_x509_crt_ptr(); - 0 - }, - Err(e) => e.to_int(), - } - } + self.ca_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_ca_cb( self.into(), Some(ca_callback::), &**self.ca_callback.as_mut().unwrap() as *const _ as *mut c_void) } + } - unsafe { - ssl_conf_ca_cb( - &mut self.inner, - Some(ca_callback::), - cb as *mut F as _, - ) - } + #[cfg(not(feature = "threading"))] + pub fn set_ca_callback(&mut self, cb: F) + where + F: Fn(&Certificate, &mut ForeignOwnedCertListBuilder) -> Result<()> + 'static, + { + self.ca_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_ca_cb( self.into(), Some(ca_callback::), &**self.ca_callback.as_mut().unwrap() as *const _ as *mut c_void) } + } + + #[cfg(feature = "threading")] + pub fn set_dbg_callback(&mut self, cb: F) + where + F: (Fn(i32, &str, i32, &str) -> ()) + Send + Sync + 'static, + { + self.dbg_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_dbg(self.into(), Some(dbg_callback::), &**self.dbg_callback.as_mut().unwrap() as *const _ as *mut c_void) } + } + + #[cfg(not(feature = "threading"))] + pub fn set_dbg_callback(&mut self, cb: F) + where + F: (Fn(i32, &str, i32, &str) -> ()) + 'static, + { + self.dbg_callback = Some(Arc::new(cb)); + unsafe { ssl_conf_dbg(self.into(), Some(dbg_callback::), &**self.dbg_callback.as_mut().unwrap() as *const _ as *mut c_void) } + } + +} + + +impl Drop for Config { + fn drop(&mut self) { + unsafe { ssl_config_free(self.into()); } } } /// Builds a linked list of x509_crt instances, all of which are owned by mbedtls. That is, the /// memory for these certificates has been allocated by mbedtls, on the C heap. This is needed for /// situations in which an mbedtls function takes ownership of a list of certs. The problem with -/// handing such functions a "normal" cert list such as certificate::LinkedCertificate or +/// handing such functions a "normal" cert list such as certificate::Certificate or /// certificate::List, is that those lists (at least partly) consist of memory allocated on the /// rust-side and hence cannot be freed on the c-side. pub struct ForeignOwnedCertListBuilder { @@ -354,10 +426,16 @@ impl ForeignOwnedCertListBuilder { } } - pub fn push_back(&mut self, cert: &LinkedCertificate) { + pub fn push_back(&mut self, cert: &Certificate) { self.try_push_back(cert.as_der()).expect("cert is a valid DER-encoded certificate"); } + pub fn try_push_back_pem(&mut self, cert: &[u8]) -> Result<()> { + // x509_crt_parse will allocate memory for the cert on the C heap + unsafe { x509_crt_parse(self.cert_list, cert.as_ptr(), cert.len()) }.into_result()?; + Ok(()) + } + pub fn try_push_back(&mut self, cert: &[u8]) -> Result<()> { // x509_crt_parse_der will allocate memory for the cert on the C heap unsafe { x509_crt_parse_der(self.cert_list, cert.as_ptr(), cert.len()) }.into_result()?; @@ -383,33 +461,131 @@ impl Drop for ForeignOwnedCertListBuilder { } } -setter_callback!(Config<'c>::set_rng(f: crate::rng::Random) = ssl_conf_rng); -setter_callback!(Config<'c>::set_dbg(f: DbgCallback) = ssl_conf_dbg); +unsafe extern "C" fn verify_callback( + closure: *mut c_void, + crt: *mut x509_crt, + depth: c_int, + flags: *mut u32, +) -> c_int +where + F: Fn(&Certificate, i32, &mut VerifyError) -> Result<()> + 'static, +{ + if crt == ::core::ptr::null_mut() || closure == ::core::ptr::null_mut() || flags == ::core::ptr::null_mut() { + return ::mbedtls_sys::ERR_X509_BAD_INPUT_DATA; + } -define!( - #[c_ty(ssl_key_cert)] - struct KeyCert; - impl<'a> UnsafeFrom {} -); + let cb = &mut *(closure as *mut F); + let crt: &mut Certificate = UnsafeFrom::from(crt).expect("valid certificate"); + + let mut verify_error = match VerifyError::from_bits(*flags) { + Some(ve) => ve, + // This can only happen if mbedtls is setting flags in VerifyError that are + // missing from our definition. + None => return ::mbedtls_sys::ERR_X509_BAD_INPUT_DATA, + }; + + let res = cb(crt, depth, &mut verify_error); + *flags = verify_error.bits(); + match res { + Ok(()) => 0, + Err(e) => e.to_int(), + } +} + + +unsafe extern "C" fn ca_callback( + closure: *mut c_void, + child: *const x509_crt, + candidate_cas: *mut *mut x509_crt +) -> c_int +where + F: Fn(&Certificate, &mut ForeignOwnedCertListBuilder) -> Result<()> + 'static, +{ + if child == ::core::ptr::null_mut() || closure == ::core::ptr::null_mut() || candidate_cas == ::core::ptr::null_mut() { + return ::mbedtls_sys::ERR_X509_BAD_INPUT_DATA; + } + + let cb = &mut *(closure as *mut F); + let crt: &Certificate = UnsafeFrom::from(child).expect("valid certificate"); + let mut cert_builder = ForeignOwnedCertListBuilder::new(); + match cb(&crt, &mut cert_builder) { + Ok(()) => { + *candidate_cas = cert_builder.to_x509_crt_ptr(); + 0 + }, + Err(e) => e.to_int(), + } +} + +unsafe extern "C" fn dbg_callback( + closure: *mut c_void, + level: c_int, + file: *const c_char, + line: c_int, + message: *const c_char +) -> () +where + F: (Fn(i32, &str, i32, &str) -> ()) + 'static, +{ + if file == ::core::ptr::null_mut() || message == ::core::ptr::null_mut() { + return (); + } -pub struct KeyCertIter<'a> { - key_cert: Option<&'a KeyCert>, + let cb = &mut *(closure as *mut F); + + let file = match std::ffi::CStr::from_ptr(file).to_str() { + Ok(text) => text, + Err(_) => return (), + }; + + let message = match std::ffi::CStr::from_ptr(message).to_str() { + Ok(text) => text, + Err(_) => return (), + }; + + + cb(level, file, line, message); } -impl<'a> Iterator for KeyCertIter<'a> { - type Item = (certificate::Iter<'a>, &'a Pk); +unsafe extern "C" fn sni_callback( + closure: *mut c_void, + ctx: *mut ssl_context, + name: *const c_uchar, + name_len: size_t, +) -> c_int +where + F: Fn(&mut HandshakeContext, &[u8]) -> Result<()> + 'static, +{ + // This is called from: + // + // mbedtls/src/ssl/context.rs - establish + // mbedtls-sys/vendor/library/ssl_tls.c - mbedtls_ssl_handshake + // mbedtls-sys/vendor/library/ssl_tls.c - mbedtls_ssl_handshake_step + // mbedtls-sys/vendor/library/ssl_srv.c - mbedtls_ssl_handshake_server_step + // mbedtls-sys/vendor/library/ssl_srv.c - ssl_parse_client_hello + // mbedtls-sys/vendor/library/ssl_srv.c - ssl_parse_servername_ext + // + // As such: + // - The ssl_context is a rust 'Context' structure that we have a mutable reference to via 'establish' + // - We can pointer cast to it to allow storing additional objects. + // + if closure == ::core::ptr::null_mut() || ctx == ::core::ptr::null_mut() || name == ::core::ptr::null_mut() { + return ::mbedtls_sys::ERR_X509_BAD_INPUT_DATA; + } - fn next(&mut self) -> Option { - self.key_cert.take().map(|key_cert| unsafe { - self.key_cert = UnsafeFrom::from(key_cert.inner.next as *const _); - ( - UnsafeFrom::from(key_cert.inner.cert as *const _).expect("not null"), - UnsafeFrom::from(key_cert.inner.key as *const _).expect("not null"), - ) - }) + let cb = &mut *(closure as *mut F); + let context = UnsafeFrom::from(ctx).unwrap(); + + let mut ctx = HandshakeContext::init(context); + + let name = from_raw_parts(name, name_len); + match cb(&mut ctx, name) { + Ok(()) => 0, + Err(_) => -1, } } + // TODO // ssl_conf_export_keys_cb // ssl_conf_dtls_cookies diff --git a/mbedtls/src/ssl/context.rs b/mbedtls/src/ssl/context.rs index cbc17534a..f9f61c88a 100644 --- a/mbedtls/src/ssl/context.rs +++ b/mbedtls/src/ssl/context.rs @@ -6,42 +6,31 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ -#[cfg(not(feature = "std"))] -use core_io::{self as io, Read, Write}; -#[cfg(feature = "std")] -use std::io::{self, Read, Write}; - use mbedtls_sys::types::raw_types::{c_int, c_uchar, c_void}; use mbedtls_sys::types::size_t; use mbedtls_sys::*; - -use crate::error::{Error, IntoResult, Result}; +use std::io::{Read, Write}; +use crate::error::{Error, Result, IntoResult}; +use std::sync::Arc; +use crate::ssl::config::{Config, Version, AuthMode}; use core::result::Result as StdResult; +use crate::x509::VerifyError; +use crate::pk::Pk; +use crate::x509::Crl; +use std::any::Any; +use crate::alloc::{List as MbedtlsList}; +use crate::x509::Certificate; use crate::private::UnsafeFrom; -use crate::ssl::config::{AuthMode, Config, Version}; -use crate::x509::{Crl, LinkedCertificate, VerifyError}; pub trait IoCallback { - unsafe extern "C" fn call_recv( - user_data: *mut c_void, - data: *mut c_uchar, - len: size_t, - ) -> c_int; - unsafe extern "C" fn call_send( - user_data: *mut c_void, - data: *const c_uchar, - len: size_t, - ) -> c_int; - + unsafe extern "C" fn call_recv(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int where Self: Sized; + unsafe extern "C" fn call_send(user_data: *mut c_void, data: *const c_uchar, len: size_t) -> c_int where Self: Sized; fn data_ptr(&mut self) -> *mut c_void; + fn as_any(&mut self) -> &mut dyn Any; } -impl IoCallback for IO { - unsafe extern "C" fn call_recv( - user_data: *mut c_void, - data: *mut c_uchar, - len: size_t, - ) -> c_int { +impl IoCallback for IO { + unsafe extern "C" fn call_recv(user_data: *mut c_void, data: *mut c_uchar, len: size_t) -> c_int { let len = if len > (c_int::max_value() as size_t) { c_int::max_value() as size_t } else { @@ -53,11 +42,7 @@ impl IoCallback for IO { } } - unsafe extern "C" fn call_send( - user_data: *mut c_void, - data: *const c_uchar, - len: size_t, - ) -> c_int { + unsafe extern "C" fn call_send(user_data: *mut c_void, data: *const c_uchar, len: size_t) -> c_int { let len = if len > (c_int::max_value() as size_t) { c_int::max_value() as size_t } else { @@ -72,65 +57,98 @@ impl IoCallback for IO { fn data_ptr(&mut self) -> *mut c_void { self as *mut IO as *mut _ } + + fn as_any(&mut self) -> &mut dyn Any { + self + } } + define!( #[c_ty(ssl_context)] - struct Context<'config>; - const init: fn() -> Self = ssl_init; - const drop: fn(&mut Self) = ssl_free; + #[repr(C)] + struct Context { + // config is used read-only for mutliple contexts and is immutable once configured. + config: Option>, + + // Must be held in heap and pointer to it as pointer is sent to MbedSSL and can't be re-allocated. + io: Option>, + + handshake_ca_cert: Option>>, + handshake_crl: Option>, + + handshake_cert: Vec>>, + handshake_pk: Vec>, + }; impl<'a> Into {} + + // Only use this when you know the type you are casting is originally a rust allocated 'Context'. impl<'a> UnsafeFrom {} ); -pub struct Session<'ctx> { - inner: &'ctx mut ssl_context, -} +#[allow(dead_code)] +impl Context { + pub fn new(config: Arc) -> Self { + let mut inner = ssl_context::default(); + + unsafe { + ssl_init(&mut inner); + ssl_setup(&mut inner, (&*config).into()); + }; -#[cfg(feature = "threading")] -unsafe impl<'ctx> Send for Session<'ctx> {} + Context { + inner, + config: Some(config.clone()), + io: None, -pub struct HandshakeContext<'ctx> { - inner: &'ctx mut ssl_context, -} - -impl<'config> Context<'config> { - pub fn new(config: &'config Config) -> Result> { - let mut ret = Self::init(); - unsafe { ssl_setup(&mut ret.inner, config.into()) } - .into_result() - .map(|_| ret) + handshake_ca_cert: None, + handshake_crl: None, + + handshake_cert: vec![], + handshake_pk: vec![], + } } - pub fn establish<'c, F: IoCallback>( - &'c mut self, - io: &'c mut F, - hostname: Option<&str>, - ) -> Result> { + pub fn establish(&mut self, io: T, hostname: Option<&str>) -> Result<()> { + self.handshake_cert.clear(); + self.handshake_pk.clear(); + self.handshake_ca_cert = None; + self.handshake_crl = None; + unsafe { - ssl_session_reset(&mut self.inner).into_result()?; + let mut io = Box::new(io); + + ssl_session_reset(self.into()).into_result()?; self.set_hostname(hostname)?; + let ptr = &mut *io as *mut _ as *mut c_void; ssl_set_bio( - &mut self.inner, - io.data_ptr(), - Some(F::call_send), - Some(F::call_recv), + self.into(), + ptr, + Some(T::call_send), + Some(T::call_recv), None, ); - match ssl_handshake(&mut self.inner).into_result() { + + self.io = Some(io); + + match ssl_handshake(self.into()).into_result() { Err(e) => { // safely end borrow of io - ssl_set_bio(&mut self.inner, ::core::ptr::null_mut(), None, None, None); + ssl_set_bio(self.into(), ::core::ptr::null_mut(), None, None, None); + self.io = None; Err(e) + }, + Ok(_) => { + Ok(()) } - Ok(_) => Ok(Session { - inner: &mut self.inner, - }), } } } +} +impl Context { + #[allow(dead_code)] #[cfg(not(feature = "std"))] fn set_hostname(&mut self, hostname: Option<&str>) -> Result<()> { match hostname { @@ -139,16 +157,13 @@ impl<'config> Context<'config> { } } + #[allow(dead_code)] #[cfg(feature = "std")] fn set_hostname(&mut self, hostname: Option<&str>) -> Result<()> { - if self.inner.hostname != ::core::ptr::null_mut() { - // potential MEMORY LEAK! See https://github.com/ARMmbed/mbedtls/issues/836 - self.inner.hostname = ::core::ptr::null_mut(); - } if let Some(s) = hostname { let cstr = ::std::ffi::CString::new(s).map_err(|_| Error::SslBadInputData)?; unsafe { - ssl_set_hostname(&mut self.inner, cstr.as_ptr()) + ssl_set_hostname(self.into(), cstr.as_ptr()) .into_result() .map(|_| ()) } @@ -158,70 +173,37 @@ impl<'config> Context<'config> { } pub fn verify_result(&self) -> StdResult<(), VerifyError> { - match unsafe { ssl_get_verify_result(&self.inner) } { + match unsafe { ssl_get_verify_result(self.into()) } { 0 => Ok(()), flags => Err(VerifyError::from_bits_truncate(flags)), } } - pub fn config(&self) -> &'config Config { - unsafe { UnsafeFrom::from(self.inner.conf).expect("not null") } - } -} - -impl<'ctx> HandshakeContext<'ctx> { - pub fn set_authmode(&mut self, am: AuthMode) { - unsafe { ssl_set_hs_authmode(self.inner, am.into()) } + pub fn config(&self) -> &Option> { + &self.config } - - pub fn set_ca_list>( - &mut self, - list: Option, - crl: Option<&'ctx mut Crl>, - ) { + + #[allow(dead_code)] + pub fn close(&mut self) { unsafe { - ssl_set_hs_ca_chain( - self.inner, - list.map(Into::into) - .map(Into::into) - .unwrap_or(::core::ptr::null_mut()), - crl.map(Into::into).unwrap_or(::core::ptr::null_mut()), - ) + ssl_close_notify(self.into()); + ssl_set_bio(self.into(), ::core::ptr::null_mut(), None, None, None); + self.io = None; } } - /// If this is never called, will use the set of private keys and - /// certificates configured in the `Config` associated with this `Context`. - /// If this is called at least once, all those are ignored and the set - /// specified using this function is used. - pub fn push_cert>( - &mut self, - chain: C, - key: &'ctx mut crate::pk::Pk, - ) -> Result<()> { - unsafe { - ssl_set_hs_own_cert(self.inner, chain.into().into(), key.into()) - .into_result() - .map(|_| ()) + pub fn get_mut_io(&mut self) -> std::io::Result<&mut T> { + match self.io.as_mut() { + Some(stream) => { + match stream.as_any().downcast_mut::() { + Some(s) => Ok(s), + None => Err(std::io::Error::new(std::io::ErrorKind::NotFound, "Peer kind is not expected")), + } + } + None => Err(std::io::Error::new(std::io::ErrorKind::NotFound, "No peer available")), } } -} - -impl<'ctx> ::core::ops::Deref for HandshakeContext<'ctx> { - type Target = Context<'ctx>; - - fn deref(&self) -> &Context<'ctx> { - unsafe { UnsafeFrom::from(&*self.inner as *const _).expect("not null") } - } -} - -impl<'ctx> UnsafeFrom<*mut ssl_context> for HandshakeContext<'ctx> { - unsafe fn from(ctx: *mut ssl_context) -> Option> { - ctx.as_mut().map(|ctx| HandshakeContext { inner: ctx }) - } -} - -impl<'a> Session<'a> { + /// Return the minor number of the negotiated TLS version pub fn minor_version(&self) -> i32 { self.inner.minor_ver @@ -235,7 +217,7 @@ impl<'a> Session<'a> { /// Return the number of bytes currently available to read that /// are stored in the Session's internal read buffer pub fn bytes_available(&self) -> usize { - unsafe { ssl_get_bytes_avail(self.inner) } + unsafe { ssl_get_bytes_avail(self.into()) } } pub fn version(&self) -> Version { @@ -251,32 +233,46 @@ impl<'a> Session<'a> { } } + + // Session specific functions + /// Return the 16-bit ciphersuite identifier. /// All assigned ciphersuites are listed by the IANA in /// https://www.iana.org/assignments/tls-parameters/tls-parameters.txt - pub fn ciphersuite(&self) -> u16 { + pub fn ciphersuite(&self) -> Result { if self.inner.session == ::core::ptr::null_mut() { - 0 - } else { - unsafe { self.inner.session.as_ref().unwrap().ciphersuite as u16 } + return Err(Error::SslBadInputData); } + + Ok(unsafe { self.inner.session.as_ref().unwrap().ciphersuite as u16 }) } - pub fn peer_cert(&self) -> Option { - unsafe { UnsafeFrom::from(ssl_get_peer_cert(self.inner)) } + // This makes a copy of the certificate as peer_cert may be destroyed outside our control. + pub fn peer_cert(&self) -> Result>> { + if self.inner.session == ::core::ptr::null_mut() { + return Err(Error::SslBadInputData); + } + + unsafe { + let peer_cert : &MbedtlsList = UnsafeFrom::from(&((*self.inner.session).peer_cert) as *const *mut x509_crt as *const *const x509_crt).ok_or(Error::SslBadInputData)?; + Ok(Some(peer_cert)) + } } +} - pub fn verify_result(&self) -> StdResult<(), VerifyError> { - match unsafe { ssl_get_verify_result(self.inner) } { - 0 => Ok(()), - flags => Err(VerifyError::from_bits_truncate(flags)), +impl Drop for Context { + fn drop(&mut self) { + unsafe { + ssl_close_notify(self.into()); + ssl_set_bio(self.into(), ::core::ptr::null_mut(), None, None, None); + ssl_free(self.into()); } } } -impl<'a> Read for Session<'a> { - fn read(&mut self, buf: &mut [u8]) -> io::Result { - match unsafe { ssl_read(self.inner, buf.as_mut_ptr(), buf.len()).into_result() } { +impl Read for Context { + fn read(&mut self, buf: &mut [u8]) -> std::io::Result { + match unsafe { ssl_read(self.into(), buf.as_mut_ptr(), buf.len()).into_result() } { Err(Error::SslPeerCloseNotify) => Ok(0), Err(e) => Err(crate::private::error_to_io_error(e)), Ok(i) => Ok(i as usize), @@ -284,29 +280,105 @@ impl<'a> Read for Session<'a> { } } -impl<'a> Write for Session<'a> { - fn write(&mut self, buf: &[u8]) -> io::Result { - match unsafe { ssl_write(self.inner, buf.as_ptr(), buf.len()).into_result() } { +impl Write for Context { + fn write(&mut self, buf: &[u8]) -> std::io::Result { + match unsafe { ssl_write(self.into(), buf.as_ptr(), buf.len()).into_result() } { Err(Error::SslPeerCloseNotify) => Ok(0), Err(e) => Err(crate::private::error_to_io_error(e)), Ok(i) => Ok(i as usize), } } - fn flush(&mut self) -> io::Result<()> { + fn flush(&mut self) -> std::io::Result<()> { Ok(()) } } -impl<'a> Drop for Session<'a> { - fn drop(&mut self) { + +pub struct HandshakeContext<'ctx> { + pub context: &'ctx mut Context, +} + +/// +/// Class exists only during SNI callback that is configured from Config. +/// SNI Callback must provide input whos lifetime exceed the SNI closure to avoid memory corruptions. +/// That can be achieved easily by storing certificate chains/crls inside the closure for the lifetime of the closure. +/// +/// That is due to SNI being held by an Arc inside Config. +/// Config lives longer then Context. Context lives longer then Handshake. +/// +/// Alternatives are not possible due to: +/// - mbedtls not providing any callbacks on handshake finish. +/// - no reasonable way to obtain a storage within the sni callback tied to the handshake or to the rust Context. (without resorting to a unscalable map or pointer magic that mbedtls may invalidate) +/// +impl<'ctx> HandshakeContext<'ctx> { + + pub fn init(context: &'ctx mut Context) -> Self { + HandshakeContext { context } + } + + pub fn set_authmode(&mut self, am: AuthMode) -> Result<()> { + if self.context.inner.handshake as *const _ == ::core::ptr::null() { + return Err(Error::SslBadInputData); + } + + unsafe { ssl_set_hs_authmode(self.context.into(), am as i32) } + Ok(()) + } + + /// @warning: chain/crl must live the same as the SNI callback otherwise risk of dangling pointers. + pub fn set_ca_list( + &mut self, + chain: Arc>, + crl: Option>, + ) -> Result<()> { + // mbedtls_ssl_set_hs_ca_chain does not check for NULL handshake. + if self.context.inner.handshake as *const _ == ::core::ptr::null() { + return Err(Error::SslBadInputData); + } + + // This will override current handshake CA chain. unsafe { - ssl_close_notify(self.inner); - ssl_set_bio(self.inner, ::core::ptr::null_mut(), None, None, None); + ssl_set_hs_ca_chain( + self.context.into(), + chain.inner_ffi_mut(), + crl.as_ref().map(|crl| crl.inner_ffi_mut()).unwrap_or(::core::ptr::null_mut()), + ); } + + self.context.handshake_ca_cert = Some(chain); + self.context.handshake_crl = crl; + Ok(()) + } + + /// If this is never called, will use the set of private keys and + /// certificates configured in the `Config` associated with this `Context`. + /// If this is called at least once, all those are ignored and the set + /// specified using this function is used. + /// + /// @warning: chain/key must live the same as the SNI callback otherwise risk of dangling pointers. + pub fn push_cert( + &mut self, + chain: Arc>, + key: Arc, + ) -> Result<()> { + // mbedtls_ssl_set_hs_own_cert does not check for NULL handshake. + if self.context.inner.handshake as *const _ == ::core::ptr::null() { + return Err(Error::SslBadInputData); + } + + // This will append provided certificate pointers in internal structures. + unsafe { + ssl_set_hs_own_cert(self.context.into(), chain.inner_ffi_mut(), key.inner_ffi_mut()).into_result()?; + } + self.context.handshake_cert.push(chain); + self.context.handshake_pk.push(key); + + Ok(()) } } + // ssl_get_alpn_protocol // ssl_get_max_frag_len // ssl_get_record_expansion diff --git a/mbedtls/src/ssl/mod.rs b/mbedtls/src/ssl/mod.rs index ea79153b1..430d439ea 100644 --- a/mbedtls/src/ssl/mod.rs +++ b/mbedtls/src/ssl/mod.rs @@ -14,8 +14,8 @@ pub mod ticket; #[doc(inline)] pub use self::ciphersuites::CipherSuite; #[doc(inline)] -pub use self::config::{Config, Version}; +pub use self::config::{Config, Version, UseSessionTickets}; #[doc(inline)] -pub use self::context::{Context, HandshakeContext, Session}; +pub use self::context::Context; #[doc(inline)] pub use self::ticket::TicketContext; diff --git a/mbedtls/src/ssl/ticket.rs b/mbedtls/src/ssl/ticket.rs index 77ce26777..8710edf07 100644 --- a/mbedtls/src/ssl/ticket.rs +++ b/mbedtls/src/ssl/ticket.rs @@ -11,6 +11,8 @@ use crate::error::{IntoResult, Result}; use mbedtls_sys::types::raw_types::{c_int, c_uchar, c_void}; use mbedtls_sys::types::size_t; use mbedtls_sys::*; +use std::sync::Arc; +use crate::rng::RngCallback; pub trait TicketCallback { unsafe extern "C" fn call_write( @@ -20,46 +22,58 @@ pub trait TicketCallback { end: *const c_uchar, tlen: *mut size_t, lifetime: *mut u32, - ) -> c_int; + ) -> c_int where Self: Sized; unsafe extern "C" fn call_parse( p_ticket: *mut c_void, session: *mut ssl_session, buf: *mut c_uchar, len: size_t, - ) -> c_int; + ) -> c_int where Self: Sized; - fn data_ptr(&mut self) -> *mut c_void; + fn data_ptr(&self) -> *mut c_void; } + define!( - #[c_ty(ssl_ticket_context)] - struct TicketContext<'rng>; - const init: fn() -> Self = ssl_ticket_init; + #[c_box_ty(ssl_ticket_context)] + #[repr(C)] + struct TicketContext { + // We set rng from constructur, we never read it directly. It is only used to ensure rng lives as long as we need. + #[allow(dead_code)] + rng: Arc, + }; const drop: fn(&mut Self) = ssl_ticket_free; + impl<'a> Into {} ); -impl<'rng> TicketContext<'rng> { - pub fn new( - rng: &'rng mut F, +#[cfg(feature = "threading")] +unsafe impl Sync for TicketContext {} + +impl TicketContext { + pub fn new( + rng: Arc, cipher: CipherType, lifetime: u32, - ) -> Result> { - let mut ret = Self::init(); + ) -> Result { + + let mut inner = Box::new(ssl_ticket_context::default()); + unsafe { + ssl_ticket_init(&mut *inner); ssl_ticket_setup( - &mut ret.inner, - Some(F::call), + &mut *inner, + Some(T::call), rng.data_ptr(), cipher.into(), lifetime, - ) + ).into_result()?; } - .into_result() - .map(|_| ret) + + Ok(TicketContext { inner, rng }) } } -impl<'rng> TicketCallback for TicketContext<'rng> { +impl TicketCallback for TicketContext { unsafe extern "C" fn call_write( p_ticket: *mut c_void, session: *const ssl_session, @@ -80,7 +94,7 @@ impl<'rng> TicketCallback for TicketContext<'rng> { ssl_ticket_parse(p_ticket, session, buf, len) } - fn data_ptr(&mut self) -> *mut c_void { - &mut self.inner as *mut _ as *mut _ + fn data_ptr(&self) -> *mut c_void { + self.handle() as *const _ as *mut _ } } diff --git a/mbedtls/src/wrapper_macros.rs b/mbedtls/src/wrapper_macros.rs index dba58ea33..b777b1e3c 100644 --- a/mbedtls/src/wrapper_macros.rs +++ b/mbedtls/src/wrapper_macros.rs @@ -15,48 +15,90 @@ macro_rules! as_item { macro_rules! callback { //{ ($($arg:ident: $ty:ty),*) -> $ret:ty } => { //}; - { $n:ident$( : $sync:ident )*($($arg:ident: $ty:ty),*) -> $ret:ty } => { + { $n:ident, $m:ident$( : $sync:ident )*($($arg:ident: $ty:ty),*) -> $ret:ty } => { #[cfg(not(feature="threading"))] pub trait $n { - unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret; + unsafe extern "C" fn call_mut(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized; - fn data_ptr(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void; + fn data_ptr_mut(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void; } #[cfg(feature="threading")] pub trait $n $( : $sync )* { - unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret; + unsafe extern "C" fn call_mut(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized; - fn data_ptr(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void; + fn data_ptr_mut(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void; } #[cfg(not(feature="threading"))] impl $n for F where F: FnMut($($ty),*) -> $ret { - unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret { + unsafe extern "C" fn call_mut(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized { (&mut*(user_data as *mut F))($($arg),*) } - fn data_ptr(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void { + fn data_ptr_mut(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void { self as *mut F as *mut _ } } #[cfg(feature="threading")] impl $n for F where F: Sync + FnMut($($ty),*) -> $ret { - unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret { + unsafe extern "C" fn call_mut(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized { (&mut*(user_data as *mut F))($($arg),*) } - fn data_ptr(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void { - self as *mut F as *mut _ + fn data_ptr_mut(&mut self) -> *mut ::mbedtls_sys::types::raw_types::c_void { + self as *const F as *mut _ + } + } + + #[cfg(not(feature="threading"))] + pub trait $m { + unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized; + + fn data_ptr(&self) -> *mut ::mbedtls_sys::types::raw_types::c_void; + } + + #[cfg(feature="threading")] + pub trait $m $( : $sync )* { + unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized; + + fn data_ptr(&self) -> *mut ::mbedtls_sys::types::raw_types::c_void; + } + + #[cfg(not(feature="threading"))] + impl $m for F where F: $n + Fn($($ty),*) -> $ret { + unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized { + (&mut*(user_data as *mut F))($($arg),*) + } + + fn data_ptr(&self) -> *mut ::mbedtls_sys::types::raw_types::c_void { + self as *const F as *mut F as *mut _ + } + } + + #[cfg(feature="threading")] + impl $m for F where F: $n + Sync + Fn($($ty),*) -> $ret { + unsafe extern "C" fn call(user_data: *mut ::mbedtls_sys::types::raw_types::c_void, $($arg:$ty),*) -> $ret where Self: Sized { + (&mut*(user_data as *mut F))($($arg),*) + } + + fn data_ptr(&self) -> *mut ::mbedtls_sys::types::raw_types::c_void { + self as *const F as *mut _ } } }; } macro_rules! define { - { #[c_ty($inner:ident)] $(#[$m:meta])* struct $name:ident$(<$l:tt>)*; $($defs:tt)* } => { - define_struct!(define $(#[$m])* struct $name $(lifetime $l)* inner $inner); + // When using members, careful with UnsafeFrom, the data casted back must have been allocated on rust side. + { #[c_ty($inner:ident)] $(#[$m:meta])* struct $name:ident$(<$l:tt>)* $({ $($(#[$mm:meta])* $member:ident: $member_type:ty,)* })?; $($defs:tt)* } => { + define_struct!(define $(#[$m])* struct $name $(lifetime $l)* inner $inner members $($($(#[$mm])* $member: $member_type,)*)*); + define_struct!(<< $name $(lifetime $l)* inner $inner >> $($defs)*); + }; + // Do not use UnsafeFrom with 'c_box_ty'. That is currently not supported as its not needed anywhere, support may be added in the future if needed anywhere. + { #[c_box_ty($inner:ident)] $(#[$m:meta])* struct $name:ident$(<$l:tt>)* $({ $($(#[$mm:meta])* $member:ident: $member_type:ty,)* })?; $($defs:tt)* } => { + define_struct!(define_box $(#[$m])* struct $name $(lifetime $l)* inner $inner members $($($(#[$mm])* $member: $member_type,)*)*); define_struct!(<< $name $(lifetime $l)* inner $inner >> $($defs)*); }; { #[c_ty($raw:ty)] $(#[$m:meta])* enum $n:ident { $(#[$doc:meta] $rust:ident = $c:ident,)* } } => { define_enum!( $(#[$m])* enum $n ty $raw : $(doc ($doc) rust $rust c $c),*); }; @@ -102,13 +144,14 @@ macro_rules! define_enum { } macro_rules! define_struct { - { define $(#[$m:meta])* struct $name:ident $(lifetime $l:tt)* inner $inner:ident } => { + { define $(#[$m:meta])* struct $name:ident $(lifetime $l:tt)* inner $inner:ident members $($(#[$mm:meta])* $member:ident: $member_type:ty,)* } => { as_item!( #[allow(dead_code)] $(#[$m])* pub struct $name<$($l)*> { inner: ::mbedtls_sys::$inner, $(r: ::core::marker::PhantomData<&$l ()>,)* + $($(#[$mm])* $member: $member_type,)* } ); @@ -137,6 +180,36 @@ macro_rules! define_struct { ); }; + { define_box $(#[$m:meta])* struct $name:ident $(lifetime $l:tt)* inner $inner:ident members $($(#[$mm:meta])* $member:ident: $member_type:ty,)* } => { + as_item!( + #[allow(dead_code)] + $(#[$m])* + pub struct $name<$($l)*> { + inner: Box<::mbedtls_sys::$inner>, + $(r: ::core::marker::PhantomData<&$l ()>,)* + $($(#[$mm])* $member: $member_type,)* + } + ); + + as_item!( + #[allow(dead_code)] + impl<$($l)*> $name<$($l)*> { + pub(crate) fn handle(&self) -> &::mbedtls_sys::$inner { + &*self.inner + } + + pub(crate) fn handle_mut(&mut self) -> &mut ::mbedtls_sys::$inner { + &mut *self.inner + } + } + ); + + as_item!( + #[cfg(feature="threading")] + unsafe impl<$($l)*> Send for $name<$($l)*> {} + ); + }; + { << $name:ident $(lifetime $l:tt)* inner $inner:ident >> const init: fn() -> Self = $ctor:ident; $($defs:tt)* } => { define_struct!(init $name () init $ctor $(lifetime $l)* ); define_struct!(<< $name $(lifetime $l)* inner $inner >> $($defs)*); @@ -172,7 +245,7 @@ macro_rules! define_struct { as_item!( impl<$($l)*> Drop for $name<$($l)*> { fn drop(&mut self) { - unsafe{::mbedtls_sys::$dtor(&mut self.inner)}; + unsafe{::mbedtls_sys::$dtor(self.handle_mut())}; } } ); @@ -186,7 +259,7 @@ macro_rules! define_struct { as_item!( impl<$l2,$($l),*> Into<*const $inner> for &$l2 $name<$($l)*> { fn into(self) -> *const $inner { - &self.inner + self.handle() } } ); @@ -194,7 +267,17 @@ macro_rules! define_struct { as_item!( impl<$l2,$($l),*> Into<*mut $inner> for &$l2 mut $name<$($l)*> { fn into(self) -> *mut $inner { - &mut self.inner + self.handle_mut() + } + } + ); + as_item!( + impl<$($l),*> $name<$($l)*> { + /// Needed for compatibility with mbedtls - where we could pass + /// `*const` but function signature requires `*mut` + #[allow(dead_code)] + pub(crate) unsafe fn inner_ffi_mut(&self) -> *mut $inner { + self.handle() as *const _ as *mut $inner } } ); @@ -235,24 +318,6 @@ macro_rules! setter { } } -// can't make this work without as as_XXX! macro, and there is no as_method!... -macro_rules! setter_callback { - { $(#[$m:meta])* $s:ident<$l:tt>::$rfn:ident($n:ident : $($rty:tt)+) = $cfn:ident } => { - as_item!( - impl<$l> $s<$l> { - $(#[$m])* - pub fn $rfn(&mut self, $n: Option<&$l mut F>) { - unsafe{::mbedtls_sys::$cfn( - &mut self.inner, - $n.as_ref().map(|_|F::call as _), - $n.map(|f|f.data_ptr()).unwrap_or(::core::ptr::null_mut()) - )} - } - } - ); - } -} - macro_rules! getter { { $(#[$m:meta])* $rfn:ident() -> $rty:ty = .$cfield:ident } => { $(#[$m])* diff --git a/mbedtls/src/x509/certificate.rs b/mbedtls/src/x509/certificate.rs index 65506557f..8a466b35c 100644 --- a/mbedtls/src/x509/certificate.rs +++ b/mbedtls/src/x509/certificate.rs @@ -7,25 +7,30 @@ * according to those terms. */ use core::fmt; -use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; -use core::ptr; +use core::ptr::NonNull; +use std::fmt::Debug; +use crate::alloc::{List as MbedtlsList, Box as MbedtlsBox}; #[cfg(not(feature = "std"))] use crate::alloc_prelude::*; +use crate::error::{Error, IntoResult, Result}; +use crate::hash::Type as MdType; +use crate::pk::Pk; +use crate::private::UnsafeFrom; +use crate::rng::Random; +use crate::x509::Time; +use core::iter::FromIterator; -use mbedtls_sys::types::raw_types::c_char; use mbedtls_sys::*; +use mbedtls_sys::types::raw_types::c_char; + +extern "C" { + pub fn forward_mbedtls_calloc(n: mbedtls_sys::types::size_t, size: mbedtls_sys::types::size_t) -> *mut mbedtls_sys::types::raw_types::c_void; +} #[cfg(feature = "std")] use yasna::{BERDecodable, BERReader, ASN1Result, ASN1Error, ASN1ErrorKind, models::ObjectIdentifier}; -use crate::pk::Pk; -use crate::error::{Error, IntoResult, Result}; -use crate::private::UnsafeFrom; -use crate::rng::Random; -use crate::hash::Type as MdType; - #[derive(Debug,Copy,Clone,Eq,PartialEq)] pub enum CertificateVersion { V1, @@ -33,13 +38,6 @@ pub enum CertificateVersion { V3 } -define!( - #[c_ty(x509_crt)] - struct Certificate; - const init: fn() -> Self = x509_crt_init; - const drop: fn(&mut Self) = x509_crt_free; -); - #[cfg(feature = "std")] #[derive(Debug, Clone, Eq, PartialEq)] pub struct Extension { @@ -60,92 +58,18 @@ impl BERDecodable for Extension { } } -impl Certificate { - pub fn from_der(der: &[u8]) -> Result { - let mut ret = Self::init(); - unsafe { x509_crt_parse_der(&mut ret.inner, der.as_ptr(), der.len()) }.into_result()?; - Ok(ret) - } - - /// Input must be NULL-terminated - pub fn from_pem(pem: &[u8]) -> Result { - let mut ret = Self::init(); - unsafe { x509_crt_parse(&mut ret.inner, pem.as_ptr(), pem.len()) }.into_result()?; - let mut fake = Self::init(); - ::core::mem::swap(&mut fake.inner.next, &mut ret.inner.next); - Ok(ret) - } - - /// Input must be NULL-terminated - #[cfg(buggy)] - pub fn from_pem_multiple(pem: &[u8]) -> Result> { - let mut vec; - unsafe { - // first, find out how many certificates we're parsing - let mut dummy = Certificate::init(); - x509_crt_parse(&mut dummy.inner, pem.as_ptr(), pem.len()).into_result()?; - - // then allocate enough certs with our allocator - vec = Vec::new(); - let mut cur: *mut _ = &mut dummy.inner; - while cur != ::core::ptr::null_mut() { - vec.push(Certificate::init()); - cur = (*cur).next; - } - - // link them together, they will become unlinked again when List drops - let list = List::from_vec(&mut vec).unwrap(); - - // load the data again but into our allocated list - x509_crt_parse(&mut list.head.inner, pem.as_ptr(), pem.len()).into_result()?; - }; - Ok(vec) - } -} - -impl fmt::Debug for Certificate { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match crate::private::alloc_string_repeat(|buf, size| unsafe { - x509_crt_info(buf, size, b"\0".as_ptr() as *const _, &self.inner) - }) { - Err(_) => Err(fmt::Error), - Ok(s) => f.write_str(&s), - } - } -} - -impl Clone for Certificate { - fn clone(&self) -> Certificate { - let mut ret = Self::init(); - unsafe { - x509_crt_parse_der(&mut ret.inner, self.inner.raw.p, self.inner.raw.len) - .into_result() - .unwrap() - }; - ret - } -} - -impl Deref for Certificate { - type Target = LinkedCertificate; - fn deref(&self) -> &LinkedCertificate { - unsafe { UnsafeFrom::from(&self.inner as *const _).unwrap() } - } -} - -impl DerefMut for Certificate { - fn deref_mut(&mut self) -> &mut LinkedCertificate { - unsafe { UnsafeFrom::from(&mut self.inner as *mut _).unwrap() } - } -} -#[repr(C)] -pub struct LinkedCertificate { - inner: x509_crt, -} +define!( + #[c_ty(x509_crt)] + #[repr(transparent)] + struct Certificate; + const drop: fn(&mut Self) = x509_crt_free; + impl<'a> Into {} + impl<'a> UnsafeFrom {} +); fn x509_buf_to_vec(buf: &x509_buf) -> Vec { - if buf.p == core::ptr::null_mut() || buf.len == 0 { + if buf.p.is_null() || buf.len == 0 { return vec![]; } @@ -153,16 +77,46 @@ fn x509_buf_to_vec(buf: &x509_buf) -> Vec { slice.to_owned() } -fn x509_time_to_time(tm: &x509_time) -> Result { +fn x509_time_to_time(tm: &x509_time) -> Result