Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for in-memory certificates #384

Merged
merged 1 commit into from
Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
17 changes: 17 additions & 0 deletions curl-sys/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
45 changes: 45 additions & 0 deletions examples/ssl_cert_blob.rs
Original file line number Diff line number Diff line change
@@ -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<Path>) -> Result<Vec<u8>> {
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::<Vec<_>>();
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(())
}
25 changes: 25 additions & 0 deletions src/easy/handle.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -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<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
self.inner.capath(path)
Expand Down
68 changes: 68 additions & 0 deletions src/easy/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -934,6 +934,17 @@ impl<H> Easy2<H> {
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> {
kulshrax marked this conversation as resolved.
Show resolved Hide resolved
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
Expand All @@ -943,6 +954,17 @@ impl<H> Easy2<H> {
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
Expand Down Expand Up @@ -1929,6 +1951,18 @@ impl<H> Easy2<H> {
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
Expand Down Expand Up @@ -1957,6 +1991,18 @@ impl<H> Easy2<H> {
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
Expand Down Expand Up @@ -2121,6 +2167,18 @@ impl<H> Easy2<H> {
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
Expand Down Expand Up @@ -2957,6 +3015,16 @@ impl<H> Easy2<H> {
}
}

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<Option<&[u8]>, Error> {
unsafe {
let p = self.getopt_ptr(opt)?;
Expand Down
19 changes: 19 additions & 0 deletions systest/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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" => {
Expand Down