Browse Source

Merge branch 'root_cert'

pull/5/head
Rodolphe Breard 5 years ago
parent
commit
8a0dd48cbd
  1. 24
      acmed/src/acme_proto.rs
  2. 3
      acmed/src/acme_proto/account.rs
  3. 42
      acmed/src/acme_proto/http.rs
  4. 15
      acmed/src/main.rs
  5. 10
      acmed/src/main_event_loop.rs

24
acmed/src/acme_proto.rs

@ -66,7 +66,7 @@ macro_rules! set_empty_data_builder {
};
}
pub fn request_certificate(cert: &Certificate) -> Result<(), Error> {
pub fn request_certificate(cert: &Certificate, root_certs: &[String]) -> Result<(), Error> {
let domains = cert
.domains
.iter()
@ -75,26 +75,26 @@ pub fn request_certificate(cert: &Certificate) -> Result<(), Error> {
let mut hook_datas = vec![];
// 1. Get the directory
let directory = http::get_directory(&cert.remote_url)?;
let directory = http::get_directory(root_certs, &cert.remote_url)?;
// 2. Get a first nonce
let nonce = http::get_nonce(&directory.new_nonce)?;
let nonce = http::get_nonce(root_certs, &directory.new_nonce)?;
// 3. Get or create the account
let (account, nonce) = AccountManager::new(cert, &directory, &nonce)?;
let (account, nonce) = AccountManager::new(cert, &directory, &nonce, root_certs)?;
// 4. Create a new order
let new_order = NewOrder::new(&domains);
let new_order = serde_json::to_string(&new_order)?;
let data_builder = set_data_builder!(account, new_order.as_bytes(), directory.new_order);
let (order, order_url, mut nonce): (Order, String, String) =
http::get_obj_loc(&directory.new_order, &data_builder, &nonce)?;
http::get_obj_loc(root_certs, &directory.new_order, &data_builder, &nonce)?;
// 5. Get all the required authorizations
for auth_url in order.authorizations.iter() {
let data_builder = set_empty_data_builder!(account, auth_url);
let (auth, new_nonce): (Authorization, String) =
http::get_obj(&auth_url, &data_builder, &nonce)?;
http::get_obj(root_certs, &auth_url, &data_builder, &nonce)?;
nonce = new_nonce;
if auth.status == AuthorizationStatus::Valid {
@ -123,7 +123,7 @@ pub fn request_certificate(cert: &Certificate) -> Result<(), Error> {
// 8. Tell the server the challenge has been completed
let chall_url = challenge.get_url();
let data_builder = set_data_builder!(account, b"{}", chall_url);
let new_nonce = http::post_challenge_response(&chall_url, &data_builder, &nonce)?;
let new_nonce = http::post_challenge_response(root_certs, &chall_url, &data_builder, &nonce)?;
nonce = new_nonce;
}
}
@ -132,7 +132,7 @@ pub fn request_certificate(cert: &Certificate) -> Result<(), Error> {
let data_builder = set_empty_data_builder!(account, auth_url);
let break_fn = |a: &Authorization| a.status == AuthorizationStatus::Valid;
let (_, new_nonce): (Authorization, String) =
http::pool_obj(&auth_url, &data_builder, &break_fn, &nonce)?;
http::pool_obj(root_certs, &auth_url, &data_builder, &break_fn, &nonce)?;
nonce = new_nonce;
}
@ -140,26 +140,26 @@ pub fn request_certificate(cert: &Certificate) -> Result<(), Error> {
let data_builder = set_empty_data_builder!(account, order_url);
let break_fn = |o: &Order| o.status == OrderStatus::Ready;
let (order, nonce): (Order, String) =
http::pool_obj(&order_url, &data_builder, &break_fn, &nonce)?;
http::pool_obj(root_certs, &order_url, &data_builder, &break_fn, &nonce)?;
// 11. Finalize the order by sending the CSR
let (priv_key, pub_key) = certificate::get_key_pair(cert)?;
let csr = certificate::generate_csr(cert, &priv_key, &pub_key)?;
let data_builder = set_data_builder!(account, csr.as_bytes(), order.finalize);
let (_, nonce): (Order, String) = http::get_obj(&order.finalize, &data_builder, &nonce)?;
let (_, nonce): (Order, String) = http::get_obj(root_certs, &order.finalize, &data_builder, &nonce)?;
// 12. Pool the order in order to see whether or not it is valid
let data_builder = set_empty_data_builder!(account, order_url);
let break_fn = |o: &Order| o.status == OrderStatus::Valid;
let (order, nonce): (Order, String) =
http::pool_obj(&order_url, &data_builder, &break_fn, &nonce)?;
http::pool_obj(root_certs, &order_url, &data_builder, &break_fn, &nonce)?;
// 13. Download the certificate
let crt_url = order
.certificate
.ok_or_else(|| Error::from("No certificate available for download."))?;
let data_builder = set_empty_data_builder!(account, crt_url);
let (crt, _) = http::get_certificate(&crt_url, &data_builder, &nonce)?;
let (crt, _) = http::get_certificate(root_certs, &crt_url, &data_builder, &nonce)?;
storage::write_certificate(cert, &crt.as_bytes())?;
for (data, hook_type) in hook_datas.iter() {

3
acmed/src/acme_proto/account.rs

@ -20,6 +20,7 @@ impl AccountManager {
cert: &Certificate,
directory: &Directory,
nonce: &str,
root_certs: &[String],
) -> Result<(Self, String), Error> {
// TODO: store the key id (account url)
let (priv_key, pub_key) = if storage::account_files_exists(cert) {
@ -42,7 +43,7 @@ impl AccountManager {
let data_builder =
|n: &str| encode_jwk(&priv_key, account.as_bytes(), &directory.new_account, n);
let (acc_rep, account_url, nonce): (AccountResponse, String, String) =
http::get_obj_loc(&directory.new_account, &data_builder, &nonce)?;
http::get_obj_loc(root_certs, &directory.new_account, &data_builder, &nonce)?;
let ac = AccountManager {
priv_key,
pub_key,

42
acmed/src/acme_proto/http.rs

@ -6,6 +6,7 @@ use http_req::uri::Uri;
use log::{debug, trace, warn};
use std::str::FromStr;
use std::{thread, time};
use std::path::Path;
const CONTENT_TYPE_JOSE: &str = "application/jose+json";
const CONTENT_TYPE_JSON: &str = "application/json";
@ -24,7 +25,7 @@ impl FromStr for DummyString {
}
}
fn new_request(uri: &Uri, method: Method) -> Request {
fn new_request<'a>(root_certs: &'a [String], uri: &'a Uri, method: Method) -> Request<'a> {
debug!("{}: {}", method, uri);
let useragent = format!(
"{}/{} ({}) {}",
@ -34,6 +35,9 @@ fn new_request(uri: &Uri, method: Method) -> Request {
env!("ACMED_HTTP_LIB_AGENT")
);
let mut rb = Request::new(uri);
for file_name in root_certs.iter() {
rb.root_cert_file_pem(&Path::new(file_name));
}
rb.method(method);
rb.header("User-Agent", &useragent);
// TODO: allow to configure the language
@ -93,9 +97,9 @@ fn nonce_from_response(res: &Response) -> Result<String, Error> {
}
}
fn post_jose_type(url: &str, data: &[u8], accept_type: &str) -> Result<(Response, String), Error> {
fn post_jose_type(root_certs: &[String], url: &str, data: &[u8], accept_type: &str) -> Result<(Response, String), Error> {
let uri = url.parse::<Uri>()?;
let mut request = new_request(&uri, Method::POST);
let mut request = new_request(root_certs, &uri, Method::POST);
request.header("Content-Type", CONTENT_TYPE_JOSE);
request.header("Content-Length", &data.len().to_string());
request.header("Accept", accept_type);
@ -116,6 +120,7 @@ fn check_response(res: &Response, body: &str) -> Result<(), AcmeError> {
}
fn fetch_obj_type<T, G>(
root_certs: &[String],
url: &str,
data_builder: &G,
nonce: &str,
@ -128,7 +133,7 @@ where
let mut nonce = nonce.to_string();
for _ in 0..crate::DEFAULT_HTTP_FAIL_NB_RETRY {
let data = data_builder(&nonce)?;
let (res, res_body) = post_jose_type(url, data.as_bytes(), accept_type)?;
let (res, res_body) = post_jose_type(root_certs, url, data.as_bytes(), accept_type)?;
nonce = nonce_from_response(&res)?;
match check_response(&res, &res_body) {
@ -150,15 +155,16 @@ where
Err("Too much errors, will not retry".into())
}
fn fetch_obj<T, G>(url: &str, data_builder: &G, nonce: &str) -> Result<(T, String, String), Error>
fn fetch_obj<T, G>(root_certs: &[String], url: &str, data_builder: &G, nonce: &str) -> Result<(T, String, String), Error>
where
T: std::str::FromStr<Err = Error>,
G: Fn(&str) -> Result<String, Error>,
{
fetch_obj_type(url, data_builder, nonce, CONTENT_TYPE_JSON)
fetch_obj_type(root_certs, url, data_builder, nonce, CONTENT_TYPE_JSON)
}
pub fn get_obj_loc<T, G>(
root_certs: &[String],
url: &str,
data_builder: &G,
nonce: &str,
@ -167,7 +173,7 @@ where
T: std::str::FromStr<Err = Error>,
G: Fn(&str) -> Result<String, Error>,
{
let (obj, location, nonce) = fetch_obj(url, data_builder, nonce)?;
let (obj, location, nonce) = fetch_obj(root_certs, url, data_builder, nonce)?;
if location.is_empty() {
Err("Location header not found.".into())
} else {
@ -175,16 +181,17 @@ where
}
}
pub fn get_obj<T, G>(url: &str, data_builder: &G, nonce: &str) -> Result<(T, String), Error>
pub fn get_obj<T, G>(root_certs: &[String], url: &str, data_builder: &G, nonce: &str) -> Result<(T, String), Error>
where
T: std::str::FromStr<Err = Error>,
G: Fn(&str) -> Result<String, Error>,
{
let (obj, _, nonce) = fetch_obj(url, data_builder, nonce)?;
let (obj, _, nonce) = fetch_obj(root_certs, url, data_builder, nonce)?;
Ok((obj, nonce))
}
pub fn pool_obj<T, G, S>(
root_certs: &[String],
url: &str,
data_builder: &G,
break_fn: &S,
@ -198,7 +205,7 @@ where
let mut nonce: String = nonce.to_string();
for _ in 0..crate::DEFAULT_POOL_NB_TRIES {
thread::sleep(time::Duration::from_secs(crate::DEFAULT_POOL_WAIT_SEC));
let (obj, _, new_nonce) = fetch_obj(url, data_builder, &nonce)?;
let (obj, _, new_nonce) = fetch_obj(root_certs, url, data_builder, &nonce)?;
if break_fn(&obj) {
return Ok((obj, new_nonce));
}
@ -208,15 +215,16 @@ where
Err(msg.into())
}
pub fn post_challenge_response<G>(url: &str, data_builder: &G, nonce: &str) -> Result<String, Error>
pub fn post_challenge_response<G>(root_certs: &[String], url: &str, data_builder: &G, nonce: &str) -> Result<String, Error>
where
G: Fn(&str) -> Result<String, Error>,
{
let (_, _, nonce): (DummyString, String, String) = fetch_obj(url, data_builder, nonce)?;
let (_, _, nonce): (DummyString, String, String) = fetch_obj(root_certs, url, data_builder, nonce)?;
Ok(nonce)
}
pub fn get_certificate<G>(
root_certs: &[String],
url: &str,
data_builder: &G,
nonce: &str,
@ -224,22 +232,22 @@ pub fn get_certificate<G>(
where
G: Fn(&str) -> Result<String, Error>,
{
let (res_body, _, nonce): (DummyString, String, String) = fetch_obj(url, data_builder, nonce)?;
let (res_body, _, nonce): (DummyString, String, String) = fetch_obj(root_certs, url, data_builder, nonce)?;
Ok((res_body.content, nonce))
}
pub fn get_directory(url: &str) -> Result<Directory, Error> {
pub fn get_directory(root_certs: &[String], url: &str) -> Result<Directory, Error> {
let uri = url.parse::<Uri>()?;
let mut request = new_request(&uri, Method::GET);
let mut request = new_request(root_certs, &uri, Method::GET);
request.header("Accept", CONTENT_TYPE_JSON);
let (r, s) = send_request_retry(&request)?;
check_response(&r, &s)?;
Directory::from_str(&s)
}
pub fn get_nonce(url: &str) -> Result<String, Error> {
pub fn get_nonce(root_certs: &[String], url: &str) -> Result<String, Error> {
let uri = url.parse::<Uri>()?;
let request = new_request(&uri, Method::HEAD);
let request = new_request(root_certs, &uri, Method::HEAD);
let (res, res_body) = send_request_retry(&request)?;
check_response(&res, &res_body)?;
nonce_from_response(&res)

15
acmed/src/main.rs

@ -75,6 +75,14 @@ fn main() {
.value_name("FILE")
.conflicts_with("foregroung"),
)
.arg(
Arg::with_name("root-cert")
.long("root-cert")
.help("Add a root certificate to the trust store")
.takes_value(true)
.multiple(true)
.value_name("FILE")
)
.get_matches();
match acme_common::logs::set_log_system(
@ -89,13 +97,18 @@ fn main() {
}
};
let root_certs = match matches.values_of("root-cert") {
Some(v) => v.collect(),
None => vec![],
};
init_server(
matches.is_present("foregroung"),
matches.value_of("pid-file").unwrap_or(DEFAULT_PID_FILE),
);
let config_file = matches.value_of("config").unwrap_or(DEFAULT_CONFIG_FILE);
let mut srv = match MainEventLoop::new(&config_file) {
let mut srv = match MainEventLoop::new(&config_file, &root_certs) {
Ok(s) => s,
Err(e) => {
error!("{}", e);

10
acmed/src/main_event_loop.rs

@ -9,10 +9,11 @@ use std::time::Duration;
pub struct MainEventLoop {
certs: Vec<Certificate>,
root_certs: Vec<String>,
}
impl MainEventLoop {
pub fn new(config_file: &str) -> Result<Self, Error> {
pub fn new(config_file: &str, root_certs: &[&str]) -> Result<Self, Error> {
let cnf = config::from_file(config_file)?;
let mut certs = Vec::new();
@ -43,7 +44,10 @@ impl MainEventLoop {
certs.push(cert);
}
Ok(MainEventLoop { certs })
Ok(MainEventLoop {
certs,
root_certs: root_certs.iter().map(|v| v.to_string()).collect(),
})
}
pub fn run(&mut self) {
@ -53,7 +57,7 @@ impl MainEventLoop {
match crt.should_renew() {
Ok(sr) => {
if sr {
let status = match request_certificate(crt) {
let status = match request_certificate(crt, &self.root_certs) {
Ok(_) => "Success.".to_string(),
Err(e) => {
let msg = format!(

Loading…
Cancel
Save