Skip to content

Commit

Permalink
Add support for in-memory certificates (#384)
Browse files Browse the repository at this point in the history
  • Loading branch information
kulshrax authored Apr 15, 2021
1 parent 64e8fc2 commit 81e3622
Show file tree
Hide file tree
Showing 6 changed files with 179 additions and 0 deletions.
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> {
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

0 comments on commit 81e3622

Please sign in to comment.