diff --git a/Cargo.toml b/Cargo.toml index de0a87b266..f04ccbbb75 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,8 @@ harness = false name = "ssl_proxy" path = "examples/ssl_proxy.rs" required-features = ["ssl"] + +[[example]] +name = "ssl_cert_blob" +path = "examples/ssl_cert_blob.rs" +required-features = ["ssl"] diff --git a/curl-sys/lib.rs b/curl-sys/lib.rs index 71c09c2e4e..78580b23d2 100644 --- a/curl-sys/lib.rs +++ b/curl-sys/lib.rs @@ -130,6 +130,16 @@ pub struct curl_fileinfo { pub b_used: size_t, } +pub const CURL_BLOB_NOCOPY: c_uint = 0; +pub const CURL_BLOB_COPY: c_uint = 1; + +#[repr(C)] +pub struct curl_blob { + pub data: *mut c_void, + pub len: size_t, + pub flags: c_uint, +} + pub const CURL_CHUNK_BGN_FUNC_OK: c_long = 0; pub const CURL_CHUNK_BGN_FUNC_FAIL: c_long = 1; pub const CURL_CHUNK_BGN_FUNC_SKIP: c_long = 2; @@ -367,6 +377,7 @@ pub const CURLOPTTYPE_LONG: CURLoption = 0; pub const CURLOPTTYPE_OBJECTPOINT: CURLoption = 10_000; pub const CURLOPTTYPE_FUNCTIONPOINT: CURLoption = 20_000; pub const CURLOPTTYPE_OFF_T: CURLoption = 30_000; +pub const CURLOPTTYPE_BLOB: CURLoption = 40_000; pub const CURLOPT_FILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 1; pub const CURLOPT_URL: CURLoption = CURLOPTTYPE_OBJECTPOINT + 2; @@ -585,6 +596,12 @@ pub const CURLOPT_PROXY_SSLKEY: CURLoption = CURLOPTTYPE_OBJECTPOINT + 256; pub const CURLOPT_MAXAGE_CONN: CURLoption = CURLOPTTYPE_LONG + 288; +pub const CURLOPT_SSLCERT_BLOB: CURLoption = CURLOPTTYPE_BLOB + 291; +pub const CURLOPT_SSLKEY_BLOB: CURLoption = CURLOPTTYPE_BLOB + 292; +pub const CURLOPT_PROXY_SSLCERT_BLOB: CURLoption = CURLOPTTYPE_BLOB + 293; +pub const CURLOPT_PROXY_SSLKEY_BLOB: CURLoption = CURLOPTTYPE_BLOB + 294; +pub const CURLOPT_ISSUERCERT_BLOB: CURLoption = CURLOPTTYPE_BLOB + 295; + pub const CURL_IPRESOLVE_WHATEVER: c_int = 0; pub const CURL_IPRESOLVE_V4: c_int = 1; pub const CURL_IPRESOLVE_V6: c_int = 2; diff --git a/examples/ssl_cert_blob.rs b/examples/ssl_cert_blob.rs new file mode 100644 index 0000000000..f1746ed725 --- /dev/null +++ b/examples/ssl_cert_blob.rs @@ -0,0 +1,45 @@ +extern crate anyhow; +extern crate curl; + +use std::env; +use std::fs::File; +use std::io::{stdout, Read, Write}; +use std::path::Path; + +use anyhow::{bail, Result}; +use curl::easy::Easy; + +fn read_file(path: impl AsRef) -> Result> { + let mut f = File::open(path)?; + let mut buf = Vec::new(); + f.read_to_end(&mut buf)?; + Ok(buf) +} + +fn main() -> Result<()> { + let argv = env::args().collect::>(); + if argv.len() < 4 { + bail!("usage: ssl_cert_blob URL CERT KEY"); + } + let url = &argv[1]; + let cert_path = &argv[2]; + let key_path = &argv[3]; + + let mut handle = Easy::new(); + + handle.url(url)?; + handle.verbose(true)?; + handle.write_function(|data| { + stdout().write_all(data).unwrap(); + Ok(data.len()) + })?; + + let cert_blob = read_file(cert_path)?; + let key_blob = read_file(key_path)?; + + handle.ssl_cert_blob(&cert_blob)?; + handle.ssl_key_blob(&key_blob)?; + + handle.perform()?; + Ok(()) +} diff --git a/src/easy/handle.rs b/src/easy/handle.rs index b3eb64b3be..f47eb053c1 100644 --- a/src/easy/handle.rs +++ b/src/easy/handle.rs @@ -588,11 +588,21 @@ impl Easy { self.inner.proxy_sslcert(sslcert) } + /// Same as [`Easy2::proxy_sslcert_blob`](struct.Easy2.html#method.proxy_sslcert_blob) + pub fn proxy_sslcert_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.inner.proxy_sslcert_blob(blob) + } + /// Same as [`Easy2::proxy_sslkey`](struct.Easy2.html#method.proxy_sslkey) pub fn proxy_sslkey(&mut self, sslkey: &str) -> Result<(), Error> { self.inner.proxy_sslkey(sslkey) } + /// Same as [`Easy2::proxy_sslkey_blob`](struct.Easy2.html#method.proxy_sslkey_blob) + pub fn proxy_sslkey_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.inner.proxy_sslkey_blob(blob) + } + /// Same as [`Easy2::proxy_type`](struct.Easy2.html#method.proxy_type) pub fn proxy_type(&mut self, kind: ProxyType) -> Result<(), Error> { self.inner.proxy_type(kind) @@ -948,6 +958,11 @@ impl Easy { self.inner.ssl_cert(cert) } + /// Same as [`Easy2::ssl_cert_blob`](struct.Easy2.html#method.ssl_cert_blob) + pub fn ssl_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.inner.ssl_cert_blob(blob) + } + /// Same as [`Easy2::ssl_cert_type`](struct.Easy2.html#method.ssl_cert_type) pub fn ssl_cert_type(&mut self, kind: &str) -> Result<(), Error> { self.inner.ssl_cert_type(kind) @@ -958,6 +973,11 @@ impl Easy { self.inner.ssl_key(key) } + /// Same as [`Easy2::ssl_key_blob`](struct.Easy2.html#method.ssl_key_blob) + pub fn ssl_key_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.inner.ssl_key_blob(blob) + } + /// Same as [`Easy2::ssl_key_type`](struct.Easy2.html#method.ssl_key_type) pub fn ssl_key_type(&mut self, kind: &str) -> Result<(), Error> { self.inner.ssl_key_type(kind) @@ -1017,6 +1037,11 @@ impl Easy { self.inner.issuer_cert(path) } + /// Same as [`Easy2::issuer_cert_blob`](struct.Easy2.html#method.issuer_cert_blob) + pub fn issuer_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.inner.issuer_cert_blob(blob) + } + /// Same as [`Easy2::capath`](struct.Easy2.html#method.capath) pub fn capath>(&mut self, path: P) -> Result<(), Error> { self.inner.capath(path) diff --git a/src/easy/handler.rs b/src/easy/handler.rs index 91181b1c8e..3bcaffdc86 100644 --- a/src/easy/handler.rs +++ b/src/easy/handler.rs @@ -934,6 +934,17 @@ impl Easy2 { self.setopt_str(curl_sys::CURLOPT_PROXY_SSLCERT, &sslcert) } + /// Set the client certificate for the proxy using an in-memory blob. + /// + /// The specified byte buffer should contain the binary content of the + /// certificate, which will be copied into the handle. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_PROXY_SSLCERT_BLOB`. + pub fn proxy_sslcert_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.setopt_blob(curl_sys::CURLOPT_PROXY_SSLCERT_BLOB, blob) + } + /// Set private key for HTTPS proxy. /// /// By default this value is not set and corresponds to @@ -943,6 +954,17 @@ impl Easy2 { self.setopt_str(curl_sys::CURLOPT_PROXY_SSLKEY, &sslkey) } + /// Set the pricate key for the proxy using an in-memory blob. + /// + /// The specified byte buffer should contain the binary content of the + /// private key, which will be copied into the handle. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_PROXY_SSLKEY_BLOB`. + pub fn proxy_sslkey_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.setopt_blob(curl_sys::CURLOPT_PROXY_SSLKEY_BLOB, blob) + } + /// Indicates the type of proxy being used. /// /// By default this option is `ProxyType::Http` and corresponds to @@ -1929,6 +1951,18 @@ impl Easy2 { self.setopt_path(curl_sys::CURLOPT_SSLCERT, cert.as_ref()) } + /// Set the SSL client certificate using an in-memory blob. + /// + /// The specified byte buffer should contain the binary content of your + /// client certificate, which will be copied into the handle. The format of + /// the certificate can be specified with `ssl_cert_type`. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_SSLCERT_BLOB`. + pub fn ssl_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.setopt_blob(curl_sys::CURLOPT_SSLCERT_BLOB, blob) + } + /// Specify type of the client SSL certificate. /// /// The string should be the format of your certificate. Supported formats @@ -1957,6 +1991,18 @@ impl Easy2 { self.setopt_path(curl_sys::CURLOPT_SSLKEY, key.as_ref()) } + /// Specify an SSL private key using an in-memory blob. + /// + /// The specified byte buffer should contain the binary content of your + /// private key, which will be copied into the handle. The format of + /// the private key can be specified with `ssl_key_type`. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_SSLKEY_BLOB`. + pub fn ssl_key_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.setopt_blob(curl_sys::CURLOPT_SSLKEY_BLOB, blob) + } + /// Set type of the private key file. /// /// The string should be the format of your private key. Supported formats @@ -2121,6 +2167,18 @@ impl Easy2 { self.setopt_path(curl_sys::CURLOPT_ISSUERCERT, path.as_ref()) } + /// Set the issuer SSL certificate using an in-memory blob. + /// + /// The specified byte buffer should contain the binary content of a CA + /// certificate in the PEM format. The certificate will be copied into the + /// handle. + /// + /// By default this option is not set and corresponds to + /// `CURLOPT_ISSUERCERT_BLOB`. + pub fn issuer_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> { + self.setopt_blob(curl_sys::CURLOPT_ISSUERCERT_BLOB, blob) + } + /// Specify directory holding CA certificates /// /// Names a directory holding multiple CA certificates to verify the peer @@ -2957,6 +3015,16 @@ impl Easy2 { } } + fn setopt_blob(&mut self, opt: curl_sys::CURLoption, val: &[u8]) -> Result<(), Error> { + let blob = curl_sys::curl_blob { + data: val.as_ptr() as *const c_void as *mut c_void, + len: val.len(), + flags: curl_sys::CURL_BLOB_COPY, + }; + let blob_ptr = &blob as *const curl_sys::curl_blob; + unsafe { self.cvt(curl_sys::curl_easy_setopt(self.inner.handle, opt, blob_ptr)) } + } + fn getopt_bytes(&mut self, opt: curl_sys::CURLINFO) -> Result, Error> { unsafe { let p = self.getopt_ptr(opt)?; diff --git a/systest/build.rs b/systest/build.rs index 42f36782f1..49157d9685 100644 --- a/systest/build.rs +++ b/systest/build.rs @@ -50,6 +50,12 @@ fn main() { cfg.skip_signededness(|s| s.ends_with("callback") || s.ends_with("function")); cfg.skip_struct(move |s| { + if version < 71 { + match s { + "curl_blob" => return true, + _ => {} + } + } if version < 70 { match s { "curl_version_info_data" => return true, @@ -73,6 +79,19 @@ fn main() { _ => {} } } + if version < 71 { + match s { + "CURLOPT_SSLCERT_BLOB" + | "CURLOPT_SSLKEY_BLOB" + | "CURLOPT_PROXY_SSLCERT_BLOB" + | "CURLOPT_PROXY_SSLKEY_BLOB" + | "CURLOPT_ISSUERCERT_BLOB" + | "CURLOPTTYPE_BLOB" + | "CURL_BLOB_NOCOPY" + | "CURL_BLOB_COPY" => return true, + _ => {} + } + } if version < 70 { match s { "CURL_VERSION_HTTP3" | "CURL_VERSION_BROTLI" | "CURLVERSION_SEVENTH" => {