diff --git a/src/conf.rs b/src/conf.rs index c4f92f1..6f437f9 100644 --- a/src/conf.rs +++ b/src/conf.rs @@ -535,10 +535,29 @@ extern "C" fn cmd_issuer_set_state_path( issuer.state_path = ptr::null_mut(); - if cf.args().get(1).map(ngx_str_t::as_bytes) == Some(b"off") { + // NGX_CONF_TAKE1 ensures that args contains 2 elements + let mut path = cf.args()[1]; + + if path.as_bytes() == b"off" { return NGX_CONF_OK; } + // We need to add our prefix before we pass the path to ngx_conf_set_path_slot, + // because otherwise it will be resolved with cycle->prefix. + if let Some(p) = issuer::NGX_ACME_STATE_PREFIX { + let mut p = ngx_str_t { + data: p.as_ptr().cast_mut(), + len: p.len(), + }; + + // ngx_get_full_name does not modify input buffers. + if !Status(unsafe { nginx_sys::ngx_get_full_name(cf.pool, &mut p, &mut path) }).is_ok() { + return NGX_CONF_ERROR; + } + + cf.args_mut()[1] = path; + } + unsafe { nginx_sys::ngx_conf_set_path_slot(cf, cmd, ptr::from_mut(issuer).cast()) } } diff --git a/src/conf/ext.rs b/src/conf/ext.rs index db28cff..642f06e 100644 --- a/src/conf/ext.rs +++ b/src/conf/ext.rs @@ -13,6 +13,7 @@ use ngx::ngx_conf_log_error; pub trait NgxConfExt { fn args(&self) -> &[ngx_str_t]; + fn args_mut(&mut self) -> &mut [ngx_str_t]; fn error(&self, dir: impl AsRef<[u8]>, err: &dyn StdError) -> *mut c_char; fn pool(&self) -> ngx::core::Pool; } @@ -23,6 +24,16 @@ impl NgxConfExt for ngx_conf_t { unsafe { self.args.as_ref().map(|x| x.as_slice()).unwrap_or_default() } } + fn args_mut(&mut self) -> &mut [ngx_str_t] { + // SAFETY: we know that cf.args is an array of ngx_str_t + unsafe { + self.args + .as_mut() + .map(|x| x.as_slice_mut()) + .unwrap_or_default() + } + } + fn error(&self, dir: impl AsRef<[u8]>, err: &dyn StdError) -> *mut c_char { // ngx_conf_log_error does not modify the `cf` itself, and the log is mutable due to being a // pointer. diff --git a/src/conf/issuer.rs b/src/conf/issuer.rs index 8af6dbf..fd17d2f 100644 --- a/src/conf/issuer.rs +++ b/src/conf/issuer.rs @@ -34,6 +34,7 @@ use crate::state::issuer::{IssuerContext, IssuerState}; use crate::time::{Time, TimeRange}; pub const ACCOUNT_URL_FILE: &str = "account.url"; +pub const NGX_ACME_STATE_PREFIX: Option<&str> = get_state_prefix(); const ACCOUNT_KEY_FILE: &str = "account.key"; const NGX_ACME_DEFAULT_RESOLVER_TIMEOUT: ngx_msec_t = 30000; @@ -344,12 +345,10 @@ fn default_state_path(cf: &mut ngx_conf_t, name: &ngx_str_t) -> Result Option<&'static str> { + const fn trim_trailing_slashes(x: &str) -> &str { + let mut bytes = x.as_bytes(); + while let [rest @ .., last] = bytes { + if *last == b'/' { + bytes = rest; + } else { + break; + } + } + // SAFETY: the transform above cannot produce an invalid UTF-8 sequence. + unsafe { core::str::from_utf8_unchecked(bytes) } + } + + const RAW_PREFIX: Option<&str> = core::option_env!("NGX_ACME_STATE_PREFIX"); + if RAW_PREFIX.is_none() { + return None; + } + + // Strip all the trailing slashes from the path. + const TRIMMED_PREFIX: &str = match RAW_PREFIX { + Some(x) => trim_trailing_slashes(x), + None => "", // unreachable + }; + + Some(constcat::concat!(TRIMMED_PREFIX, "/")) +}