Skip to content

Commit

Permalink
[ADD] Support for https redirect URIs (TLS)
Browse files Browse the repository at this point in the history
This commit adds support for TLS-encrypted redirect URIs.
The commit adds a second server for https:
1. Binding a new TCP socket
2. Uses `rcgen` to generate a new TLS certificate (only in memory)
3. Uses `rustls` to do the TLS handshake and convert the TCPStream
into a TLS stream
4. Generalizes the `request` function (and its dependent) to accept an
object implementing `Read + Write` instead of just `TCPStream`
5. Redirects all `redirect_uri` that contain `https` to the https server

Signed-off-by: Silvano Cortesi <[email protected]>
  • Loading branch information
trembel committed Sep 6, 2024
1 parent 04dc523 commit cc36f44
Show file tree
Hide file tree
Showing 10 changed files with 194 additions and 27 deletions.
47 changes: 42 additions & 5 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ url = "2"
urlencoding = "2"
wait-timeout = "0.2"
whoami = "1.5"
rustls = { version = "0.23.12", features = ["ring", "std"], default-features = false }
rcgen = { version = "0.13.1", features = ["crypto", "ring"], default-features = false }

[target.'cfg(target_os="openbsd")'.dependencies]
pledge = "0.4"
Expand Down
1 change: 1 addition & 0 deletions src/config.l
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ client_id "CLIENT_ID"
client_secret "CLIENT_SECRET"
error_notify_cmd "ERROR_NOTIFY_CMD"
http_listen "HTTP_LISTEN"
https_listen "HTTPS_LISTEN"
login_hint "LOGIN_HINT"
transient_error_if_cmd "TRANSIENT_ERROR_IF_CMD"
refresh_retry "REFRESH_RETRY"
Expand Down
50 changes: 47 additions & 3 deletions src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const REFRESH_RETRY_DEFAULT: Duration = Duration::from_secs(40);
const AUTH_NOTIFY_INTERVAL_DEFAULT: u64 = 15 * 60;
/// What is the default bind() address for the HTTP server?
const HTTP_LISTEN_DEFAULT: &str = "127.0.0.1:0";
const HTTPS_LISTEN_DEFAULT: &str = "127.0.0.1:0";

#[derive(Debug)]
pub struct Config {
Expand All @@ -34,6 +35,7 @@ pub struct Config {
pub auth_notify_interval: Duration,
pub error_notify_cmd: Option<String>,
pub http_listen: String,
pub https_listen: String,
pub transient_error_if_cmd: Option<String>,
refresh_at_least: Option<Duration>,
refresh_before_expiry: Option<Duration>,
Expand Down Expand Up @@ -69,6 +71,7 @@ impl Config {
let mut auth_notify_interval = None;
let mut error_notify_cmd = None;
let mut http_listen = None;
let mut https_listen = None;
let mut transient_error_if_cmd = None;
let mut refresh_at_least = None;
let mut refresh_before_expiry = None;
Expand Down Expand Up @@ -130,6 +133,14 @@ impl Config {
http_listen,
)?)
}
config_ast::TopLevel::HttpsListen(span) => {
https_listen = Some(check_not_assigned_str(
&lexer,
"https_listen",
span,
https_listen,
)?)
}
config_ast::TopLevel::TransientErrorIfCmd(span) => {
transient_error_if_cmd = Some(check_not_assigned_str(
&lexer,
Expand Down Expand Up @@ -188,6 +199,7 @@ impl Config {
.unwrap_or_else(|| Duration::from_secs(AUTH_NOTIFY_INTERVAL_DEFAULT)),
error_notify_cmd,
http_listen: http_listen.unwrap_or_else(|| HTTP_LISTEN_DEFAULT.to_owned()),
https_listen: https_listen.unwrap_or_else(|| HTTPS_LISTEN_DEFAULT.to_owned()),
transient_error_if_cmd,
refresh_at_least,
refresh_before_expiry,
Expand Down Expand Up @@ -482,10 +494,15 @@ impl Account {
&& self.token_uri == act_dump.token_uri
}

pub fn redirect_uri(&self, http_port: u16) -> Result<Url, Box<dyn Error>> {
pub fn redirect_uri(&self, http_port: u16, https_port: u16) -> Result<Url, Box<dyn Error>> {
let mut url = Url::parse(&self.redirect_uri)?;
url.set_port(Some(http_port))
.map_err(|_| "Cannot set port")?;
if self.redirect_uri.to_lowercase().starts_with("https") {
url.set_port(Some(https_port))
.map_err(|_| "Cannot set https port")?;
} else {
url.set_port(Some(http_port))
.map_err(|_| "Cannot set http port")?;
}
Ok(url)
}

Expand Down Expand Up @@ -786,6 +803,33 @@ mod test {
invalid_uri("token_uri");
}

#[test]
fn valid_https_config() {
let c = Config::from_str(
r#"
https_listen = "127.0.0.1:56789";
account "x" {
// Mandatory fields
auth_uri = "http://a.com";
auth_uri_fields = {"l": "m", "n": "o", "l": "p"};
client_id = "b";
scopes = ["c", "d"];
token_uri = "http://f.com";
// Optional fields
redirect_uri = "https://e.com";
}
"#,
)
.unwrap();
assert_eq!(c.https_listen, "127.0.0.1:56789".to_owned());
let act = &c.accounts["x"];
assert_eq!(act.redirect_uri, "https://e.com");
let uri = act.redirect_uri(0, 56789).unwrap();
assert_eq!(uri.scheme(), "https");
assert_eq!(uri.port(), Some(56789));
assert_eq!(uri.host_str(), Some("e.com"));
}

#[test]
fn mandatory_account_fields() {
let fields = &[
Expand Down
1 change: 1 addition & 0 deletions src/config.y
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ TopLevel -> Result<TopLevel, ()>:
| "AUTH_NOTIFY_INTERVAL" "=" "TIME" ";" { Ok(TopLevel::AuthNotifyInterval(map_err($3)?)) }
| "ERROR_NOTIFY_CMD" "=" "STRING" ";" { Ok(TopLevel::ErrorNotifyCmd(map_err($3)?)) }
| "HTTP_LISTEN" "=" "STRING" ";" { Ok(TopLevel::HttpListen(map_err($3)?)) }
| "HTTPS_LISTEN" "=" "STRING" ";" { Ok(TopLevel::HttpsListen(map_err($3)?)) }
| "TRANSIENT_ERROR_IF_CMD" "=" "STRING" ";" { Ok(TopLevel::TransientErrorIfCmd(map_err($3)?)) }
| "REFRESH_AT_LEAST" "=" "TIME" ";" { Ok(TopLevel::RefreshAtLeast(map_err($3)?)) }
| "REFRESH_BEFORE_EXPIRY" "=" "TIME" ";" { Ok(TopLevel::RefreshBeforeExpiry(map_err($3)?)) }
Expand Down
1 change: 1 addition & 0 deletions src/config_ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ pub enum TopLevel {
AuthNotifyInterval(Span),
ErrorNotifyCmd(Span),
HttpListen(Span),
HttpsListen(Span),
TransientErrorIfCmd(Span),
RefreshAtLeast(Span),
RefreshBeforeExpiry(Span),
Expand Down
Loading

0 comments on commit cc36f44

Please sign in to comment.