From afecf09ef34c6ae12ae9bced865f371c40aaa216 Mon Sep 17 00:00:00 2001 From: Adrian Cruceru Date: Mon, 30 Nov 2020 21:14:27 +0000 Subject: [PATCH 1/2] 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. --- Cargo.lock | 164 +++++ ct.sh | 1 + mbedtls-sys/Cargo.toml | 1 + mbedtls-sys/build/cmake.rs | 3 + mbedtls/Cargo.toml | 7 + mbedtls/build.rs | 6 + mbedtls/examples/client.rs | 21 +- mbedtls/examples/server.rs | 23 +- mbedtls/src/alloc.rs | 65 ++ mbedtls/src/lib.rs | 23 +- mbedtls/src/mbedtls_malloc.c | 31 + mbedtls/src/pk/dhparam.rs | 2 +- mbedtls/src/pk/mod.rs | 94 ++- mbedtls/src/pk/rfc6979.rs | 10 +- mbedtls/src/pkcs12/mod.rs | 3 +- mbedtls/src/rng/ctr_drbg.rs | 180 ++---- mbedtls/src/rng/hmac_drbg.rs | 75 ++- mbedtls/src/rng/mod.rs | 6 +- mbedtls/src/rng/os_entropy.rs | 77 ++- mbedtls/src/rng/rdrand.rs | 35 +- mbedtls/src/self_test.rs | 18 +- mbedtls/src/ssl/ciphersuites.rs | 1 + mbedtls/src/ssl/config.rs | 412 ++++++------ mbedtls/src/ssl/context.rs | 343 +++++----- mbedtls/src/ssl/mod.rs | 4 +- mbedtls/src/ssl/ticket.rs | 83 ++- mbedtls/src/wrapper_macros.rs | 283 ++++++-- mbedtls/src/x509/certificate.rs | 1026 +++++++++++++++++++++--------- mbedtls/src/x509/mod.rs | 2 +- mbedtls/tests/client_server.rs | 89 ++- mbedtls/tests/hyper.rs | 532 ++++++++++++++++ mbedtls/tests/ssl_conf_ca_cb.rs | 76 ++- mbedtls/tests/ssl_conf_verify.rs | 47 +- mbedtls/tests/support/entropy.rs | 2 +- mbedtls/tests/support/rand.rs | 18 +- mbedtls/valgrind_unittests.sh | 6 + 36 files changed, 2769 insertions(+), 1000 deletions(-) create mode 100644 mbedtls/src/alloc.rs create mode 100644 mbedtls/src/mbedtls_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/ct.sh b/ct.sh index fc3c3bd42..f34e63f2d 100755 --- a/ct.sh +++ b/ct.sh @@ -19,6 +19,7 @@ if [ $TRAVIS_RUST_VERSION = "stable" ] || [ $TRAVIS_RUST_VERSION = "beta" ] || [ cargo test --features pkcs12 cargo test --features pkcs12_rc2 cargo test --features force_aesni_support + cargo test --features default,pthread elif [ $TRAVIS_RUST_VERSION = $CORE_IO_NIGHTLY ]; then cargo +$CORE_IO_NIGHTLY test --no-default-features --features core_io,rdrand,time,custom_time,custom_gmtime_r 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..4344121fb 100644 --- a/mbedtls/Cargo.toml +++ b/mbedtls/Cargo.toml @@ -44,6 +44,7 @@ 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 +120,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 = ["std", "threading"] diff --git a/mbedtls/build.rs b/mbedtls/build.rs index 004c8a9c1..f19fd340a 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/mbedtls_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..12883277e --- /dev/null +++ b/mbedtls/src/alloc.rs @@ -0,0 +1,65 @@ +/* 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. */ + +use core::fmt; +use core::ops::{Deref, DerefMut}; +use core::ptr::NonNull; +use core::ptr::drop_in_place; +use core::mem::ManuallyDrop; + +use mbedtls_sys::types::raw_types::c_void; + +extern "C" { + pub(crate) fn forward_mbedtls_free(n: *mut mbedtls_sys::types::raw_types::c_void); +} + +#[repr(transparent)] +pub struct Box { + pub(crate) inner: NonNull +} + +impl Box { + pub(crate) fn into_raw(self) -> *mut T { + let v = ManuallyDrop::new(self); + v.inner.as_ptr() + } +} + +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..b98ca07ff 100644 --- a/mbedtls/src/lib.rs +++ b/mbedtls/src/lib.rs @@ -15,7 +15,7 @@ const ERROR: _MUST_USE_EITHER_STD_OR_CORE_IO_ = (); #[cfg(not(feature = "std"))] #[macro_use] -extern crate alloc; +extern crate alloc as rust_alloc; #[cfg(feature = "std")] extern crate core; #[cfg(not(feature = "std"))] @@ -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; @@ -112,11 +113,13 @@ mod mbedtls { #[cfg(not(feature = "std"))] mod alloc_prelude { #![allow(unused)] - pub(crate) use alloc::borrow::ToOwned; - pub(crate) use alloc::boxed::Box; - pub(crate) use alloc::string::String; - pub(crate) use alloc::string::ToString; - pub(crate) use alloc::vec::Vec; + pub(crate) use rust_alloc::borrow::ToOwned; + pub(crate) use rust_alloc::boxed::Box; + pub(crate) use rust_alloc::sync::Arc; + pub(crate) use rust_alloc::string::String; + pub(crate) use rust_alloc::string::ToString; + pub(crate) use rust_alloc::vec::Vec; + pub(crate) use rust_alloc::borrow::Cow; } #[cfg(all(feature="time", any(feature="custom_gmtime_r", feature="custom_time")))] @@ -163,3 +166,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/mbedtls_malloc.c b/mbedtls/src/mbedtls_malloc.c new file mode 100644 index 000000000..a7f051f7a --- /dev/null +++ b/mbedtls/src/mbedtls_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/pk/dhparam.rs b/mbedtls/src/pk/dhparam.rs index 6c785efb5..8f98b9d08 100644 --- a/mbedtls/src/pk/dhparam.rs +++ b/mbedtls/src/pk/dhparam.rs @@ -22,7 +22,7 @@ impl Dhm { /// Takes both DER and PEM forms of FFDH parameters in `DHParams` format. /// /// When calling on PEM-encoded data, `params` must be NULL-terminated - pub(crate) fn from_params(params: &[u8]) -> Result { + pub fn from_params(params: &[u8]) -> Result { let mut ret = Self::init(); unsafe { dhm_parse_dhm(&mut ret.inner, params.as_ptr(), params.len()) }.into_result()?; Ok(ret) diff --git a/mbedtls/src/pk/mod.rs b/mbedtls/src/pk/mod.rs index 28cb6086f..7e152ff25 100644 --- a/mbedtls/src/pk/mod.rs +++ b/mbedtls/src/pk/mod.rs @@ -35,6 +35,8 @@ pub use self::ec::{EcGroupId, ECDSA_MAX_LEN}; #[doc(inline)] pub use crate::ecp::EcGroup; +pub use dhparam::Dhm; + // SHA-256("Fortanix")[:4] const CUSTOM_PK_TYPE: pk_type_t = 0x8b205408 as pk_type_t; @@ -132,6 +134,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 +145,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 +866,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 +891,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..264a32744 100644 --- a/mbedtls/src/rng/ctr_drbg.rs +++ b/mbedtls/src/rng/ctr_drbg.rs @@ -6,126 +6,67 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ +#[cfg(feature = "std")] +use std::sync::Arc; + +pub use mbedtls_sys::CTR_DRBG_RESEED_INTERVAL as RESEED_INTERVAL; +use mbedtls_sys::*; 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}; - -// ==== 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) }; - } - } -} - -pub struct CtrDrbg<'entropy> { - boxed: Box>, -} +use crate::error::{IntoResult, Result}; +use crate::rng::{EntropyCallback, RngCallback, RngCallbackMut}; -impl<'entropy> CtrDrbg<'entropy> { - fn init() -> Self { - CtrDrbg { - boxed: Box::new(CtrDrbgInner::init()), - } - } -} +define!( + // `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 we could use c_ty instead of c_box_ty. + // + #[c_box_ty(ctr_drbg_context)] + #[repr(C)] + struct CtrDrbg { + entropy: Arc, + }; + const drop: fn(&mut Self) = ctr_drbg_free; + impl<'a> Into {} +); -#[doc(hidden)] -impl<'entropy> Deref for CtrDrbg<'entropy> { - type Target = CtrDrbgInner<'entropy>; - fn deref(&self) -> &Self::Target { - &self.boxed - } -} +// +// 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 {} -#[doc(hidden)] -impl<'entropy> DerefMut for CtrDrbg<'entropy> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.boxed - } -} +#[allow(dead_code)] +impl CtrDrbg { -// ==== END IMMOVABLE TYPE KLUDGE ==== + 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,7 +78,7 @@ 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 }, ) } @@ -151,7 +92,7 @@ impl<'entropy> CtrDrbg<'entropy> { 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,7 +104,7 @@ 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: @@ -174,13 +115,26 @@ impl<'entropy> CtrDrbg<'entropy> { // } -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: 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: 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..6589e01d9 100644 --- a/mbedtls/src/rng/hmac_drbg.rs +++ b/mbedtls/src/rng/hmac_drbg.rs @@ -6,44 +6,53 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ + +#[cfg(feature = "std")] +use std::sync::Arc; + +pub use mbedtls_sys::HMAC_DRBG_RESEED_INTERVAL as RESEED_INTERVAL; +use mbedtls_sys::*; 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 super::{EntropyCallback, RngCallback}; +#[cfg(not(feature = "std"))] +use crate::alloc_prelude::*; use crate::error::{IntoResult, Result}; use crate::hash::MdInfo; +use crate::rng::{EntropyCallback, RngCallback, RngCallbackMut}; 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; + impl<'a> Into {} ); #[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 +60,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 +132,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..5e40e958f 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(data: *mut c_uchar, len: size_t) -> c_int); +callback!(RngCallbackMut,RngCallback(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: 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..98eccf506 100644 --- a/mbedtls/src/rng/os_entropy.rs +++ b/mbedtls/src/rng/os_entropy.rs @@ -6,71 +6,106 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ +use std::sync::Arc; + +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 crate::error::{IntoResult, Result}; +use crate::rng::{EntropyCallback,EntropyCallbackMut}; -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; + #[repr(C)] + struct OsEntropy { + sources: Vec>, + }; + pub const new: fn() -> Self = entropy_init { sources: Vec::with_capacity(1), }; const drop: fn(&mut Self) = entropy_free; + impl<'a> Into {} ); +// +// 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 {} -impl<'source> OsEntropy<'source> { - pub fn add_source( +#[allow(dead_code)] +impl OsEntropy { + 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.inner_ffi_mut(), 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()?; + pub fn gather(&self) -> Result<()> { + // function is guarded with internal mutex: mbedtls-sys/vendor/crypto/library/entropy.c:310 + unsafe { entropy_gather(self.inner_ffi_mut()) }.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()?; + pub fn update_manual(&self, data: &[u8]) -> Result<()> { + // function is guarded with internal mutex: mbedtls-sys/vendor/crypto/library/entropy.c:241 + unsafe { entropy_update_manual(self.inner_ffi_mut(), 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(&self) -> *mut c_void { + &self.inner as *const _ as *mut _ + } +} + +impl EntropyCallbackMut for OsEntropy { + #[inline(always)] + unsafe extern "C" fn call_mut(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_mut(&mut 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/self_test.rs b/mbedtls/src/self_test.rs index 3a97b2244..1c9ca94d0 100644 --- a/mbedtls/src/self_test.rs +++ b/mbedtls/src/self_test.rs @@ -10,6 +10,11 @@ //! //! Calling mbedTLS self-test functions before they're enabled using the //! `enable()` function here will result in a panic. +//! +//! Using this module in multithreaded or async environment will fail. +//! Functions rely on global variables to track operations and anything non-self-test related will stomp over variables. +//! To use correctly, make sure no other code uses mbedtls. Multiple self test operations done simultaneously may also return failures. +//! #[cfg(any(target_os = "none", target_env = "sgx", not(feature = "std")))] use mbedtls_sys::types::raw_types::{c_char, c_int}; @@ -44,20 +49,29 @@ 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 +/// # Safety +/// +/// The caller needs to ensure this function is not called while any other function in this module is called.. #[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 +/// # Safety +/// +/// The caller needs to ensure this function is not called while any other function in this module is called.. #[cfg(any(target_os = "none", target_env = "sgx", not(feature = "std")))] pub unsafe fn disable() { rand_f = None; log_f = None; } +/// # Safety +/// +/// This function, if used in a multithreaded or async environment will fail. +/// Function relies on global variables to track operations and anything non-self-test related will stomp over variables. +/// To use correctly, make sure no other code uses mbedtls. Multiple self test operations done simultaneously 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..5abb4b4f9 100644 --- a/mbedtls/src/ssl/config.rs +++ b/mbedtls/src/ssl/config.rs @@ -6,25 +6,32 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ +#[cfg(feature = "std")] +use std::sync::Arc; +#[cfg(feature = "std")] +use std::borrow::Cow; + 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 mbedtls_sys::types::raw_types::*; +use mbedtls_sys::types::size_t; -use crate::error::{Error, IntoResult, Result}; -use core::result::Result as StdResult; -use crate::pk::dhparam::Dhm; + +use crate::alloc::{List as MbedtlsList}; +#[cfg(not(feature = "std"))] +use crate::alloc_prelude::*; +use crate::error::{Error, Result, IntoResult}; use crate::pk::Pk; +use crate::pk::dhparam::Dhm; use crate::private::UnsafeFrom; +use crate::rng::RngCallback; use crate::ssl::context::HandshakeContext; use crate::ssl::ticket::TicketCallback; -use crate::x509::{certificate, Crl, LinkedCertificate, Profile, VerifyError}; - -extern "C" { - fn calloc(n: usize, size: usize) -> *mut c_void; - fn free(ptr: *mut c_void); -} +use crate::x509::Certificate; +use crate::x509::Crl; +use crate::x509::Profile; +use crate::x509::VerifyError; #[allow(non_camel_case_types)] #[derive(Eq, PartialEq, PartialOrd, Ord, Debug, Copy, Clone)] @@ -83,27 +90,83 @@ 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, + } +); + +callback!(VerifyCallback: Fn(&Certificate, i32, &mut VerifyError) -> Result<()>); +#[cfg(feature = "std")] +callback!(DbgCallback: Fn(i32, Cow<'_, str>, i32, Cow<'_, str>) -> ()); +callback!(SniCallback: Fn(&mut HandshakeContext, &[u8]) -> Result<()>); +callback!(CaCallback: Fn(&MbedtlsList) -> Result>); define!( #[c_ty(ssl_config)] - struct Config<'c>; - const init: fn() -> Self = ssl_config_init; + #[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>, + + rng: Option>, + + ciphersuites: Vec>>, + curves: Option>>, + + #[allow(dead_code)] + dhm: Option>, + + verify_callback: Option>, + #[cfg(feature = "std")] + dbg_callback: Option>, + sni_callback: Option>, + ticket_callback: Option>, + ca_callback: Option>, + }; const drop: fn(&mut Self) = ssl_config_free; - impl<'q> Into {} - impl<'q> UnsafeFrom {} + 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 = 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, + #[cfg(feature = "std")] + dbg_callback: None, + sni_callback: None, + ticket_callback: None, + ca_callback: None, } - c } // need bitfield support getter!(endpoint() -> Endpoint = field endpoint); @@ -119,21 +182,30 @@ impl<'c> Config<'c> { 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); } + 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 +215,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,68 +227,59 @@ 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 + setter!(set_cert_profile(p: &'static Profile) = ssl_conf_cert_profile); /// 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)?; + pub fn set_dh_params(&mut self, dhm: Arc) -> Result<()> { 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!( @@ -224,43 +287,62 @@ impl<'c> Config<'c> { set_session_tickets(u: UseSessionTickets) = ssl_conf_session_tickets ); + setter!(set_renegotiation(u: Renegotiation) = ssl_conf_renegotiation); + 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<(), ()>, - >( + + pub fn set_sni_callback(&mut self, cb: F) + where + F: SniCallback + 'static, + { + unsafe extern "C" fn sni_callback( closure: *mut c_void, ctx: *mut ssl_context, name: *const c_uchar, name_len: size_t, - ) -> c_int { + ) -> 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. + // let cb = &mut *(closure as *mut F); - let mut ctx = UnsafeFrom::from(ctx).expect("valid context"); + 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, + Err(_) => -1, } } - unsafe { ssl_conf_sni(&mut self.inner, Some(sni_callback::), cb as *mut F as _) } + + 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) + pub fn set_verify_callback(&mut self, cb: F) where - F: FnMut(&mut LinkedCertificate, i32, &mut VerifyError) -> Result<()>, + F: VerifyCallback + 'static, { unsafe extern "C" fn verify_callback( closure: *mut c_void, @@ -269,17 +351,22 @@ impl<'c> Config<'c> { flags: *mut u32, ) -> c_int where - F: FnMut(&mut LinkedCertificate, i32, &mut VerifyError) -> Result<()>, + F: VerifyCallback + 'static, { + if crt.is_null() || closure.is_null() || flags.is_null() { + return ::mbedtls_sys::ERR_X509_BAD_INPUT_DATA; + } + let cb = &mut *(closure as *mut F); - let crt: &mut LinkedCertificate = - UnsafeFrom::from(crt).expect("valid certificate"); + 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 { @@ -288,125 +375,76 @@ impl<'c> Config<'c> { } } - unsafe { - ssl_conf_verify( - &mut self.inner, - Some(verify_callback::), - cb as *mut F as _, - ) - } + + 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) } } - pub fn set_ca_callback(&mut self, cb: &'c mut F) - where - F: FnMut(&LinkedCertificate, &mut ForeignOwnedCertListBuilder) -> Result<()>, + pub fn set_ca_callback(&mut self, cb: F) + where + F: CaCallback + '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<()>, + where + F: CaCallback + 'static, { + if child.is_null() || closure.is_null() || candidate_cas.is_null() { + return ::mbedtls_sys::ERR_X509_BAD_INPUT_DATA; + } + 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(); + let crt: &MbedtlsList = UnsafeFrom::from(&child as *const *const x509_crt).expect("valid certificate"); + match cb(&crt) { + Ok(list) => { + // This does not leak due to mbedtls taking ownership from us and freeing the certificates itself. (logic is in: mbedtls-sys/vendor/library/x509_crt.c:2904) + *candidate_cas = list.into_raw(); 0 }, Err(e) => e.to_int(), } } - unsafe { - ssl_conf_ca_cb( - &mut self.inner, - Some(ca_callback::), - cb as *mut F as _, - ) - } + 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) } } -} -/// 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 -/// 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 { - cert_list: *mut x509_crt, -} - -impl ForeignOwnedCertListBuilder { - pub(crate) fn new() -> Self { - let cert_list = unsafe { calloc(1, core::mem::size_of::()) } as *mut x509_crt; - if cert_list == ::core::ptr::null_mut() { - panic!("Out of memory"); - } - unsafe { ::mbedtls_sys::x509_crt_init(cert_list); } - - Self { - cert_list - } - } - - pub fn push_back(&mut self, cert: &LinkedCertificate) { - self.try_push_back(cert.as_der()).expect("cert is a valid DER-encoded certificate"); - } - - 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()?; - Ok(()) - } - - // The memory pointed to by the return value is managed by mbedtls. If the return value is - // dropped without handing it to an mbedtls-function that takes ownership of it, that memory - // will be leaked. - pub(crate) fn to_x509_crt_ptr(mut self) -> *mut x509_crt { - let res = self.cert_list; - self.cert_list = ::core::ptr::null_mut(); - res - } -} + #[cfg(feature = "std")] + pub fn set_dbg_callback(&mut self, cb: F) + where + F: DbgCallback + 'static, + { + #[allow(dead_code)] + 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: DbgCallback + 'static, + { + let cb = &mut *(closure as *mut F); -impl Drop for ForeignOwnedCertListBuilder { - fn drop(&mut self) { - unsafe { - ::mbedtls_sys::x509_crt_free(self.cert_list); - free(self.cert_list as *mut c_void); + let file = match file.is_null() { + false => std::ffi::CStr::from_ptr(file).to_string_lossy(), + true => Cow::from(""), + }; + + let message = match message.is_null() { + false => std::ffi::CStr::from_ptr(message).to_string_lossy(), + true => Cow::from(""), + }; + + cb(level, file, line, message); } - } -} - -setter_callback!(Config<'c>::set_rng(f: crate::rng::Random) = ssl_conf_rng); -setter_callback!(Config<'c>::set_dbg(f: DbgCallback) = ssl_conf_dbg); - -define!( - #[c_ty(ssl_key_cert)] - struct KeyCert; - impl<'a> UnsafeFrom {} -); - -pub struct KeyCertIter<'a> { - key_cert: Option<&'a KeyCert>, -} -impl<'a> Iterator for KeyCertIter<'a> { - type Item = (certificate::Iter<'a>, &'a Pk); - - 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"), - ) - }) + 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) } } } diff --git a/mbedtls/src/ssl/context.rs b/mbedtls/src/ssl/context.rs index cbc17534a..f40a31f15 100644 --- a/mbedtls/src/ssl/context.rs +++ b/mbedtls/src/ssl/context.rs @@ -6,42 +6,38 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ + +use core::any::Any; +use core::result::Result as StdResult; #[cfg(not(feature = "std"))] -use core_io::{self as io, Read, Write}; +use core_io::{Read, Write, Result as IoResult}; +#[cfg(feature = "std")] +use std::io::{Read, Write, Result as IoResult}; #[cfg(feature = "std")] -use std::io::{self, Read, Write}; +use std::sync::Arc; 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 core::result::Result as StdResult; +use crate::alloc::{List as MbedtlsList}; +#[cfg(not(feature = "std"))] +use crate::alloc_prelude::*; +use crate::error::{Error, Result, IntoResult}; +use crate::pk::Pk; 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; +use crate::ssl::config::{Config, Version, AuthMode}; +use crate::x509::{Certificate, Crl, VerifyError}; + +pub trait IoCallback : Any { + 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; } -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 +49,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 { @@ -74,59 +66,83 @@ impl IoCallback for IO { } } + 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: Arc, + + // 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, -} +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: 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<()> { 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); + + self.handshake_cert.clear(); + self.handshake_pk.clear(); + self.handshake_ca_cert = None; + self.handshake_crl = None; + + 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, - }), } } } @@ -141,14 +157,10 @@ impl<'config> Context<'config> { #[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 +170,31 @@ 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 set_ca_list>( - &mut self, - list: Option, - crl: Option<&'ctx mut Crl>, - ) { - 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()), - ) - } + pub fn config(&self) -> &Arc { + &self.config } - - /// 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<()> { + + pub fn close(&mut self) { unsafe { - ssl_set_hs_own_cert(self.inner, chain.into().into(), key.into()) - .into_result() - .map(|_| ()) + ssl_close_notify(self.into()); + ssl_set_bio(self.into(), ::core::ptr::null_mut(), None, None, None); + self.io = None; } } -} - -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") } + pub fn io(&self) -> Option<&dyn Any> { + self.io.as_ref().map(|v| &**v) } -} - -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 }) + pub fn io_mut(&mut self) -> Option<&mut dyn Any> { + self.io.as_mut().map(|v| &mut **v) } -} - -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 +208,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 +224,45 @@ 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 { - if self.inner.session == ::core::ptr::null_mut() { - 0 - } else { - unsafe { self.inner.session.as_ref().unwrap().ciphersuite as u16 } + pub fn ciphersuite(&self) -> Result { + if self.inner.session.is_null() { + 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)) } + pub fn peer_cert(&self) -> Result>> { + if self.inner.session.is_null() { + return Err(Error::SslBadInputData); + } + + unsafe { + // We cannot call the peer cert function as we need a pointer to a pointer to create the MbedtlsList, we need something in the heap / cannot use any local variable for that. + 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 { + self.close(); + 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]) -> IoResult { + 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 +270,102 @@ 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]) -> IoResult { + 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) -> IoResult<()> { 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(crate) 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(()) + } + + 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. + 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..3c7db39ba 100644 --- a/mbedtls/src/ssl/ticket.rs +++ b/mbedtls/src/ssl/ticket.rs @@ -6,12 +6,21 @@ * option. This file may not be copied, modified, or distributed except * according to those terms. */ -use crate::cipher::raw::CipherType; -use crate::error::{IntoResult, Result}; +#[cfg(feature = "std")] +use std::sync::Arc; + +use mbedtls_sys::*; use mbedtls_sys::types::raw_types::{c_int, c_uchar, c_void}; use mbedtls_sys::types::size_t; -use mbedtls_sys::*; +#[cfg(not(feature = "std"))] +use crate::alloc_prelude::*; +use crate::cipher::raw::CipherType; +use crate::error::{IntoResult, Result}; +use crate::rng::RngCallback; + + +#[cfg(not(feature = "threading"))] pub trait TicketCallback { unsafe extern "C" fn call_write( p_ticket: *mut c_void, @@ -20,46 +29,78 @@ 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; } +#[cfg(feature = "threading")] +pub trait TicketCallback : Sync { + unsafe extern "C" fn call_write( + p_ticket: *mut c_void, + session: *const ssl_session, + start: *mut c_uchar, + end: *const c_uchar, + tlen: *mut size_t, + lifetime: *mut u32, + ) -> 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 where Self: Sized; + + fn data_ptr(&self) -> *mut c_void; +} + + define!( #[c_ty(ssl_ticket_context)] - struct TicketContext<'rng>; - const init: fn() -> Self = ssl_ticket_init; + #[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 ret = TicketContext { inner: ssl_ticket_context::default(), rng }; + unsafe { + ssl_ticket_init(&mut ret.inner); ssl_ticket_setup( &mut ret.inner, - Some(F::call), - rng.data_ptr(), + Some(T::call), + ret.rng.data_ptr(), cipher.into(), lifetime, - ) + ).into_result()?; } - .into_result() - .map(|_| ret) + + Ok(ret) } } -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 +121,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..1c65d39b0 100644 --- a/mbedtls/src/wrapper_macros.rs +++ b/mbedtls/src/wrapper_macros.rs @@ -15,48 +15,101 @@ 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($($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; + pub trait $n : Sync { + 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: 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: 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 _ + } + } + }; + ($t:ident: $($bound:tt)*) => { + #[cfg(not(feature = "threading"))] + pub trait $t: $($bound)* {} + #[cfg(feature = "threading")] + pub trait $t: $($bound)* + Sync {} + + #[cfg(not(feature = "threading"))] + impl $t for F {} + #[cfg(feature = "threading")] + impl $t for F {} }; } 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 +155,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,15 +191,45 @@ macro_rules! define_struct { ); }; - { << $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_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 $({ $($member:ident: $member_init:expr,)* })?; $($defs:tt)* } => { + define_struct!(init $name () init $ctor $(lifetime $l)* members $($($member: $member_init,)*)* ); define_struct!(<< $name $(lifetime $l)* inner $inner >> $($defs)*); }; - { << $name:ident $(lifetime $l:tt)* inner $inner:ident >> pub const new: fn() -> Self = $ctor:ident; $($defs:tt)* } => { - define_struct!(init $name (pub) new $ctor $(lifetime $l)* ); + { << $name:ident $(lifetime $l:tt)* inner $inner:ident >> pub const new: fn() -> Self = $ctor:ident $({ $($member:ident: $member_init:expr,)* })?; $($defs:tt)* } => { + define_struct!(init $name (pub) new $ctor $(lifetime $l)* members $($($member: $member_init,)*)* ); define_struct!(<< $name $(lifetime $l)* inner $inner >> $($defs)*); }; - { init $name:ident ($($vis:tt)*) $new:ident $ctor:ident $(lifetime $l:tt)* } => { + { init $name:ident ($($vis:tt)*) $new:ident $ctor:ident $(lifetime $l:tt)* members $($member:ident: $member_init:expr,)* } => { as_item!( #[allow(dead_code)] impl<$($l)*> $name<$($l)*> { @@ -158,6 +242,7 @@ macro_rules! define_struct { $name{ inner:inner, $(r: ::core::marker::PhantomData::<&$l _>,)* + $($member: $member_init,)* } } } @@ -172,7 +257,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 +271,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 +279,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 } } ); @@ -230,29 +325,11 @@ macro_rules! setter { { $(#[$m:meta])* $rfn:ident($n:ident : $rty:ty) = $cfn:ident } => { $(#[$m])* pub fn $rfn(&mut self, $n: $rty) { - unsafe{::mbedtls_sys::$cfn(&mut self.inner,$n.into())} + unsafe{::mbedtls_sys::$cfn(self.into(),$n.into())} } } } -// 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])* @@ -263,7 +340,129 @@ macro_rules! getter { { $(#[$m:meta])* $rfn:ident() -> $rty:ty = fn $cfn:ident } => { $(#[$m])* pub fn $rfn(&self) -> $rty { - unsafe{::mbedtls_sys::$cfn(&self.inner).into()} + unsafe{::mbedtls_sys::$cfn(self.into()).into()} } }; } + + + +#[cfg(test)] +mod tests { + #[allow(dead_code)] + /// Utilities for testing whether types implement certain traits. + /// + /// For each trait `Trait` that you want to be able to test, you should + /// implement: + /// ```ignore + /// impl Testable for T {} + /// ``` + /// + /// Then, to test whether a type `Type` implements `Trait`, call: + /// ```ignore + /// TestTrait::::new().impls_trait() + /// ``` + /// This returns a `bool` indicating whether the trait is implemented. + // This relies on auto-deref to distinguish between types that do and don't + // implement the trait. + mod testtrait { + use core::marker::PhantomData; + + pub struct NonImplTrait { + inner: PhantomData + } + + pub struct TestTrait { + non_impl: NonImplTrait, + phantom: PhantomData<*const TraitObj>, + } + + pub trait Testable {} + + impl TestTrait { + pub fn new() -> Self { + TestTrait { non_impl: NonImplTrait { inner: PhantomData }, phantom: PhantomData } + } + } + + impl> TestTrait { + pub fn impls_trait(&self) -> bool { + true + } + } + + impl NonImplTrait { + pub fn impls_trait(&self) -> bool { + false + } + } + + impl core::ops::Deref for TestTrait { + type Target = NonImplTrait; + + fn deref(&self) -> &NonImplTrait { + &self.non_impl + } + } + } + + use testtrait::{TestTrait, Testable}; + + callback!(RustTest: Fn() -> ()); + callback!(NativeTestMut,NativeTest() -> ()); + + impl Testable for T {} + impl Testable for T {} + impl Testable for T {} + impl Testable for T {} + + #[test] + #[cfg(feature = "threading")] + fn callback_sync_with_threading() { + fn test_closure() { + assert!(TestTrait::::new().impls_trait(), "RustTest should be Sync"); + } + fn test_native_closure() { + assert!(TestTrait::::new().impls_trait(), "NativeTest should be Sync"); + } + fn test_native_mut_closure() { + assert!(TestTrait::::new().impls_trait(), "NativeTestMut should be Sync"); + } + + test_closure::()>(); + test_native_closure::()>(); + test_native_mut_closure::()>(); + + assert!(!TestTrait::()>::new().impls_trait(), "non-Sync closure shouldn't be RustTest"); + assert!(TestTrait::() + Sync)>::new().impls_trait(), "Sync closure should be RustTest"); + assert!(!TestTrait::()>::new().impls_trait(), "non-Sync closure shouldn't be NativeTest"); + assert!(TestTrait::() + Sync)>::new().impls_trait(), "Sync closure should be NativeTest"); + assert!(!TestTrait::()>::new().impls_trait(), "non-Sync closure shouldn't be NativeTestMut"); + assert!(TestTrait::() + Sync)>::new().impls_trait(), "Sync closure should be NativeTestMut"); + } + + #[test] + #[cfg(not(feature = "threading"))] + fn callback_sync_without_threading() { + fn test_closure() { + assert!(!TestTrait::::new().impls_trait(), "RustTest shouldn't be Sync"); + } + fn test_native_closure() { + assert!(!TestTrait::::new().impls_trait(), "NativeTest shouldn't be Sync"); + } + fn test_native_mut_closure() { + assert!(!TestTrait::::new().impls_trait(), "NativeTestMut shouldn't be Sync"); + } + + test_closure::()>(); + test_native_closure::()>(); + test_native_mut_closure::()>(); + + assert!(TestTrait::()>::new().impls_trait(), "non-Sync closure should be RustTest"); + assert!(TestTrait::() + Sync)>::new().impls_trait(), "Sync closure should be RustTest"); + assert!(TestTrait::()>::new().impls_trait(), "non-Sync closure should be NativeTest"); + assert!(TestTrait::() + Sync)>::new().impls_trait(), "Sync closure should be NativeTest"); + assert!(TestTrait::()>::new().impls_trait(), "non-Sync closure should be NativeTestMut"); + assert!(TestTrait::() + Sync)>::new().impls_trait(), "Sync closure should be NativeTestMut"); + } +} diff --git a/mbedtls/src/x509/certificate.rs b/mbedtls/src/x509/certificate.rs index 65506557f..6bd3dad5b 100644 --- a/mbedtls/src/x509/certificate.rs +++ b/mbedtls/src/x509/certificate.rs @@ -7,25 +7,29 @@ * according to those terms. */ use core::fmt; -use core::marker::PhantomData; -use core::ops::{Deref, DerefMut}; -use core::ptr; +use core::iter::FromIterator; +use core::ptr::NonNull; +use mbedtls_sys::*; +use mbedtls_sys::types::raw_types::c_char; + +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 mbedtls_sys::types::raw_types::c_char; -use mbedtls_sys::*; +extern "C" { + pub(crate) 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 +37,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 +57,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 +76,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