Skip to content

Commit 81e3622

Browse files
authored
Add support for in-memory certificates (#384)
1 parent 64e8fc2 commit 81e3622

File tree

6 files changed

+179
-0
lines changed

6 files changed

+179
-0
lines changed

Cargo.toml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,8 @@ harness = false
5858
name = "ssl_proxy"
5959
path = "examples/ssl_proxy.rs"
6060
required-features = ["ssl"]
61+
62+
[[example]]
63+
name = "ssl_cert_blob"
64+
path = "examples/ssl_cert_blob.rs"
65+
required-features = ["ssl"]

curl-sys/lib.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,16 @@ pub struct curl_fileinfo {
130130
pub b_used: size_t,
131131
}
132132

133+
pub const CURL_BLOB_NOCOPY: c_uint = 0;
134+
pub const CURL_BLOB_COPY: c_uint = 1;
135+
136+
#[repr(C)]
137+
pub struct curl_blob {
138+
pub data: *mut c_void,
139+
pub len: size_t,
140+
pub flags: c_uint,
141+
}
142+
133143
pub const CURL_CHUNK_BGN_FUNC_OK: c_long = 0;
134144
pub const CURL_CHUNK_BGN_FUNC_FAIL: c_long = 1;
135145
pub const CURL_CHUNK_BGN_FUNC_SKIP: c_long = 2;
@@ -367,6 +377,7 @@ pub const CURLOPTTYPE_LONG: CURLoption = 0;
367377
pub const CURLOPTTYPE_OBJECTPOINT: CURLoption = 10_000;
368378
pub const CURLOPTTYPE_FUNCTIONPOINT: CURLoption = 20_000;
369379
pub const CURLOPTTYPE_OFF_T: CURLoption = 30_000;
380+
pub const CURLOPTTYPE_BLOB: CURLoption = 40_000;
370381

371382
pub const CURLOPT_FILE: CURLoption = CURLOPTTYPE_OBJECTPOINT + 1;
372383
pub const CURLOPT_URL: CURLoption = CURLOPTTYPE_OBJECTPOINT + 2;
@@ -585,6 +596,12 @@ pub const CURLOPT_PROXY_SSLKEY: CURLoption = CURLOPTTYPE_OBJECTPOINT + 256;
585596

586597
pub const CURLOPT_MAXAGE_CONN: CURLoption = CURLOPTTYPE_LONG + 288;
587598

599+
pub const CURLOPT_SSLCERT_BLOB: CURLoption = CURLOPTTYPE_BLOB + 291;
600+
pub const CURLOPT_SSLKEY_BLOB: CURLoption = CURLOPTTYPE_BLOB + 292;
601+
pub const CURLOPT_PROXY_SSLCERT_BLOB: CURLoption = CURLOPTTYPE_BLOB + 293;
602+
pub const CURLOPT_PROXY_SSLKEY_BLOB: CURLoption = CURLOPTTYPE_BLOB + 294;
603+
pub const CURLOPT_ISSUERCERT_BLOB: CURLoption = CURLOPTTYPE_BLOB + 295;
604+
588605
pub const CURL_IPRESOLVE_WHATEVER: c_int = 0;
589606
pub const CURL_IPRESOLVE_V4: c_int = 1;
590607
pub const CURL_IPRESOLVE_V6: c_int = 2;

examples/ssl_cert_blob.rs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
extern crate anyhow;
2+
extern crate curl;
3+
4+
use std::env;
5+
use std::fs::File;
6+
use std::io::{stdout, Read, Write};
7+
use std::path::Path;
8+
9+
use anyhow::{bail, Result};
10+
use curl::easy::Easy;
11+
12+
fn read_file(path: impl AsRef<Path>) -> Result<Vec<u8>> {
13+
let mut f = File::open(path)?;
14+
let mut buf = Vec::new();
15+
f.read_to_end(&mut buf)?;
16+
Ok(buf)
17+
}
18+
19+
fn main() -> Result<()> {
20+
let argv = env::args().collect::<Vec<_>>();
21+
if argv.len() < 4 {
22+
bail!("usage: ssl_cert_blob URL CERT KEY");
23+
}
24+
let url = &argv[1];
25+
let cert_path = &argv[2];
26+
let key_path = &argv[3];
27+
28+
let mut handle = Easy::new();
29+
30+
handle.url(url)?;
31+
handle.verbose(true)?;
32+
handle.write_function(|data| {
33+
stdout().write_all(data).unwrap();
34+
Ok(data.len())
35+
})?;
36+
37+
let cert_blob = read_file(cert_path)?;
38+
let key_blob = read_file(key_path)?;
39+
40+
handle.ssl_cert_blob(&cert_blob)?;
41+
handle.ssl_key_blob(&key_blob)?;
42+
43+
handle.perform()?;
44+
Ok(())
45+
}

src/easy/handle.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,11 +588,21 @@ impl Easy {
588588
self.inner.proxy_sslcert(sslcert)
589589
}
590590

591+
/// Same as [`Easy2::proxy_sslcert_blob`](struct.Easy2.html#method.proxy_sslcert_blob)
592+
pub fn proxy_sslcert_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
593+
self.inner.proxy_sslcert_blob(blob)
594+
}
595+
591596
/// Same as [`Easy2::proxy_sslkey`](struct.Easy2.html#method.proxy_sslkey)
592597
pub fn proxy_sslkey(&mut self, sslkey: &str) -> Result<(), Error> {
593598
self.inner.proxy_sslkey(sslkey)
594599
}
595600

601+
/// Same as [`Easy2::proxy_sslkey_blob`](struct.Easy2.html#method.proxy_sslkey_blob)
602+
pub fn proxy_sslkey_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
603+
self.inner.proxy_sslkey_blob(blob)
604+
}
605+
596606
/// Same as [`Easy2::proxy_type`](struct.Easy2.html#method.proxy_type)
597607
pub fn proxy_type(&mut self, kind: ProxyType) -> Result<(), Error> {
598608
self.inner.proxy_type(kind)
@@ -948,6 +958,11 @@ impl Easy {
948958
self.inner.ssl_cert(cert)
949959
}
950960

961+
/// Same as [`Easy2::ssl_cert_blob`](struct.Easy2.html#method.ssl_cert_blob)
962+
pub fn ssl_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
963+
self.inner.ssl_cert_blob(blob)
964+
}
965+
951966
/// Same as [`Easy2::ssl_cert_type`](struct.Easy2.html#method.ssl_cert_type)
952967
pub fn ssl_cert_type(&mut self, kind: &str) -> Result<(), Error> {
953968
self.inner.ssl_cert_type(kind)
@@ -958,6 +973,11 @@ impl Easy {
958973
self.inner.ssl_key(key)
959974
}
960975

976+
/// Same as [`Easy2::ssl_key_blob`](struct.Easy2.html#method.ssl_key_blob)
977+
pub fn ssl_key_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
978+
self.inner.ssl_key_blob(blob)
979+
}
980+
961981
/// Same as [`Easy2::ssl_key_type`](struct.Easy2.html#method.ssl_key_type)
962982
pub fn ssl_key_type(&mut self, kind: &str) -> Result<(), Error> {
963983
self.inner.ssl_key_type(kind)
@@ -1017,6 +1037,11 @@ impl Easy {
10171037
self.inner.issuer_cert(path)
10181038
}
10191039

1040+
/// Same as [`Easy2::issuer_cert_blob`](struct.Easy2.html#method.issuer_cert_blob)
1041+
pub fn issuer_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
1042+
self.inner.issuer_cert_blob(blob)
1043+
}
1044+
10201045
/// Same as [`Easy2::capath`](struct.Easy2.html#method.capath)
10211046
pub fn capath<P: AsRef<Path>>(&mut self, path: P) -> Result<(), Error> {
10221047
self.inner.capath(path)

src/easy/handler.rs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -934,6 +934,17 @@ impl<H> Easy2<H> {
934934
self.setopt_str(curl_sys::CURLOPT_PROXY_SSLCERT, &sslcert)
935935
}
936936

937+
/// Set the client certificate for the proxy using an in-memory blob.
938+
///
939+
/// The specified byte buffer should contain the binary content of the
940+
/// certificate, which will be copied into the handle.
941+
///
942+
/// By default this option is not set and corresponds to
943+
/// `CURLOPT_PROXY_SSLCERT_BLOB`.
944+
pub fn proxy_sslcert_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
945+
self.setopt_blob(curl_sys::CURLOPT_PROXY_SSLCERT_BLOB, blob)
946+
}
947+
937948
/// Set private key for HTTPS proxy.
938949
///
939950
/// By default this value is not set and corresponds to
@@ -943,6 +954,17 @@ impl<H> Easy2<H> {
943954
self.setopt_str(curl_sys::CURLOPT_PROXY_SSLKEY, &sslkey)
944955
}
945956

957+
/// Set the pricate key for the proxy using an in-memory blob.
958+
///
959+
/// The specified byte buffer should contain the binary content of the
960+
/// private key, which will be copied into the handle.
961+
///
962+
/// By default this option is not set and corresponds to
963+
/// `CURLOPT_PROXY_SSLKEY_BLOB`.
964+
pub fn proxy_sslkey_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
965+
self.setopt_blob(curl_sys::CURLOPT_PROXY_SSLKEY_BLOB, blob)
966+
}
967+
946968
/// Indicates the type of proxy being used.
947969
///
948970
/// By default this option is `ProxyType::Http` and corresponds to
@@ -1929,6 +1951,18 @@ impl<H> Easy2<H> {
19291951
self.setopt_path(curl_sys::CURLOPT_SSLCERT, cert.as_ref())
19301952
}
19311953

1954+
/// Set the SSL client certificate using an in-memory blob.
1955+
///
1956+
/// The specified byte buffer should contain the binary content of your
1957+
/// client certificate, which will be copied into the handle. The format of
1958+
/// the certificate can be specified with `ssl_cert_type`.
1959+
///
1960+
/// By default this option is not set and corresponds to
1961+
/// `CURLOPT_SSLCERT_BLOB`.
1962+
pub fn ssl_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
1963+
self.setopt_blob(curl_sys::CURLOPT_SSLCERT_BLOB, blob)
1964+
}
1965+
19321966
/// Specify type of the client SSL certificate.
19331967
///
19341968
/// The string should be the format of your certificate. Supported formats
@@ -1957,6 +1991,18 @@ impl<H> Easy2<H> {
19571991
self.setopt_path(curl_sys::CURLOPT_SSLKEY, key.as_ref())
19581992
}
19591993

1994+
/// Specify an SSL private key using an in-memory blob.
1995+
///
1996+
/// The specified byte buffer should contain the binary content of your
1997+
/// private key, which will be copied into the handle. The format of
1998+
/// the private key can be specified with `ssl_key_type`.
1999+
///
2000+
/// By default this option is not set and corresponds to
2001+
/// `CURLOPT_SSLKEY_BLOB`.
2002+
pub fn ssl_key_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
2003+
self.setopt_blob(curl_sys::CURLOPT_SSLKEY_BLOB, blob)
2004+
}
2005+
19602006
/// Set type of the private key file.
19612007
///
19622008
/// The string should be the format of your private key. Supported formats
@@ -2121,6 +2167,18 @@ impl<H> Easy2<H> {
21212167
self.setopt_path(curl_sys::CURLOPT_ISSUERCERT, path.as_ref())
21222168
}
21232169

2170+
/// Set the issuer SSL certificate using an in-memory blob.
2171+
///
2172+
/// The specified byte buffer should contain the binary content of a CA
2173+
/// certificate in the PEM format. The certificate will be copied into the
2174+
/// handle.
2175+
///
2176+
/// By default this option is not set and corresponds to
2177+
/// `CURLOPT_ISSUERCERT_BLOB`.
2178+
pub fn issuer_cert_blob(&mut self, blob: &[u8]) -> Result<(), Error> {
2179+
self.setopt_blob(curl_sys::CURLOPT_ISSUERCERT_BLOB, blob)
2180+
}
2181+
21242182
/// Specify directory holding CA certificates
21252183
///
21262184
/// Names a directory holding multiple CA certificates to verify the peer
@@ -2957,6 +3015,16 @@ impl<H> Easy2<H> {
29573015
}
29583016
}
29593017

3018+
fn setopt_blob(&mut self, opt: curl_sys::CURLoption, val: &[u8]) -> Result<(), Error> {
3019+
let blob = curl_sys::curl_blob {
3020+
data: val.as_ptr() as *const c_void as *mut c_void,
3021+
len: val.len(),
3022+
flags: curl_sys::CURL_BLOB_COPY,
3023+
};
3024+
let blob_ptr = &blob as *const curl_sys::curl_blob;
3025+
unsafe { self.cvt(curl_sys::curl_easy_setopt(self.inner.handle, opt, blob_ptr)) }
3026+
}
3027+
29603028
fn getopt_bytes(&mut self, opt: curl_sys::CURLINFO) -> Result<Option<&[u8]>, Error> {
29613029
unsafe {
29623030
let p = self.getopt_ptr(opt)?;

systest/build.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,12 @@ fn main() {
5050
cfg.skip_signededness(|s| s.ends_with("callback") || s.ends_with("function"));
5151

5252
cfg.skip_struct(move |s| {
53+
if version < 71 {
54+
match s {
55+
"curl_blob" => return true,
56+
_ => {}
57+
}
58+
}
5359
if version < 70 {
5460
match s {
5561
"curl_version_info_data" => return true,
@@ -73,6 +79,19 @@ fn main() {
7379
_ => {}
7480
}
7581
}
82+
if version < 71 {
83+
match s {
84+
"CURLOPT_SSLCERT_BLOB"
85+
| "CURLOPT_SSLKEY_BLOB"
86+
| "CURLOPT_PROXY_SSLCERT_BLOB"
87+
| "CURLOPT_PROXY_SSLKEY_BLOB"
88+
| "CURLOPT_ISSUERCERT_BLOB"
89+
| "CURLOPTTYPE_BLOB"
90+
| "CURL_BLOB_NOCOPY"
91+
| "CURL_BLOB_COPY" => return true,
92+
_ => {}
93+
}
94+
}
7695
if version < 70 {
7796
match s {
7897
"CURL_VERSION_HTTP3" | "CURL_VERSION_BROTLI" | "CURLVERSION_SEVENTH" => {

0 commit comments

Comments
 (0)