diff --git a/src/proxy/auth.rs b/src/proxy/auth.rs index a70c06f9..319e8e5c 100644 --- a/src/proxy/auth.rs +++ b/src/proxy/auth.rs @@ -6,14 +6,12 @@ use crate::config::ProxyConfig; use anyhow::{Error, Result}; use async_trait::async_trait; use rsip::Header; -use rsip::Uri; use rsip::headers::UntypedHeader; -use rsip::headers::auth::Algorithm; use rsip::headers::{ProxyAuthenticate, WwwAuthenticate}; use rsip::prelude::HeadersExt; use rsip::prelude::ToTypedHeader; -use rsip::services::DigestGenerator; use rsip::typed::Authorization; +use rsipstack::dialog::authenticate::verify_digest; use rsipstack::transaction::transaction::Transaction; use std::sync::Arc; use tokio_util::sync::CancellationToken; @@ -85,22 +83,22 @@ impl AuthModule { &self, tx: &Transaction, ) -> Result, AuthError> { - let mut auth_inner: Option = None; + let mut auth_inner: Option<(Authorization, &str)> = None; for header in tx.original.headers.iter() { match header { Header::Authorization(h) => { - auth_inner = h.typed().ok(); + auth_inner = h.typed().ok().map(|auth| (auth, h.value())); break; } Header::ProxyAuthorization(h) => { - auth_inner = h.typed().ok().map(|a| a.0); + auth_inner = h.typed().ok().map(|auth| (auth.0, h.value())); break; } _ => {} } } - let auth_inner = match auth_inner { - Some(a) => a, + let (auth_inner, raw_auth_header) = match auth_inner { + Some(auth) => auth, None => { return Ok(None); } @@ -127,9 +125,9 @@ impl AuthModule { stored_user.merge_with(&user); match self.verify_credentials( &stored_user, - &tx.original.uri, &tx.original.method, &auth_inner, + raw_auth_header, ) { true => Ok(Some(stored_user)), false => Ok(None), @@ -145,28 +143,14 @@ impl AuthModule { fn verify_credentials( &self, user: &SipUser, - uri: &Uri, method: &rsip::Method, auth: &Authorization, + raw_auth_header: &str, ) -> bool { - // Use the same approach as common.rs let empty_string = "".to_string(); let password = user.password.as_ref().unwrap_or(&empty_string); - // Create a digest generator to compute the expected response - let expected_response = DigestGenerator { - username: &user.username, - password, - algorithm: auth.algorithm.unwrap_or(Algorithm::Md5), - nonce: &auth.nonce, - method, - qop: auth.qop.as_ref(), - uri, - realm: &auth.realm, - } - .compute(); - - expected_response == auth.response + verify_digest(auth, password, method, raw_auth_header) } pub fn create_proxy_auth_challenge(&self, realm: &str) -> Result { diff --git a/src/proxy/tests/test_auth.rs b/src/proxy/tests/test_auth.rs index 68b4f11e..979c77b7 100644 --- a/src/proxy/tests/test_auth.rs +++ b/src/proxy/tests/test_auth.rs @@ -3,7 +3,8 @@ use std::{sync::Arc, time::Duration}; use super::common::{ create_auth_request, create_proxy_auth_request_with_nonce, create_test_request, - create_test_server, create_transaction, extract_nonce_from_proxy_authenticate, + create_test_server, create_test_server_with_config, create_transaction, + extract_nonce_from_proxy_authenticate, }; use crate::call::{SipUser, TransactionCookie}; use crate::config::{ProxyConfig, RtpConfig}; @@ -630,6 +631,107 @@ fn create_sip_request(method: rsip::Method, username: &str, realm: &str) -> rsip request } +async fn create_issue_146_server() -> Arc { + let mut config = ProxyConfig::default(); + config.realms = Some(vec![ + "pbx.e36".to_string(), + "pbx.e36:5060".to_string(), + "pbx.e36:5061".to_string(), + ]); + + let (server, _) = create_test_server_with_config(config).await; + server + .user_backend + .create_user(SipUser { + id: 111, + username: "111".to_string(), + password: Some("111".to_string()), + enabled: true, + realm: Some("pbx.e36".to_string()), + ..Default::default() + }) + .await + .unwrap(); + server +} + +fn create_issue_146_register_request(request_uri: &str, authorization: &str) -> rsip::Request { + let request_uri = rsip::Uri::try_from(request_uri).unwrap(); + let aor_uri = rsip::Uri::try_from("sip:111@pbx.e36").unwrap(); + let contact_uri = rsip::Uri::try_from("sip:111@192.168.20.169:5060;transport=tls").unwrap(); + + let headers = vec![ + rsip::typed::From { + display_name: Some("Deskphone".into()), + uri: aor_uri.clone(), + params: vec![rsip::Param::Tag(rsip::param::Tag::new("a1a3406102"))], + } + .into(), + rsip::typed::To { + display_name: None, + uri: aor_uri, + params: vec![], + } + .into(), + rsip::headers::Via::new( + "SIP/2.0/TLS 192.168.20.169:5060;branch=z9hG4bK64964f4e5cad27a81".to_string(), + ) + .into(), + rsip::headers::CallId::new("fd9623914bbc79b9").into(), + rsip::headers::typed::CSeq { + seq: 873199510u32.into(), + method: rsip::Method::Register, + } + .into(), + rsip::typed::Contact { + display_name: Some("Deskphone".into()), + uri: contact_uri, + params: vec![rsip::Param::Expires(rsip::param::Expires::from("3600"))], + } + .into(), + rsip::headers::Authorization::new(authorization.to_string()).into(), + ]; + + rsip::Request { + method: rsip::Method::Register, + uri: request_uri, + version: rsip::Version::V2, + headers: headers.into(), + body: vec![], + } +} + +#[tokio::test] +async fn test_authenticate_request_accepts_authorization_uri_when_request_uri_differs() { + let server = create_issue_146_server().await; + let module = AuthModule::new(server); + let auth_header_value = r#"Digest username="111",realm="pbx.e36",nonce="MoLk0nzBonitjdoo",uri="sip:pbx.e36:5060;transport=udp",response="5a832a648a56b95f905b8db1d28d8f5b",algorithm=MD5"#; + + let request = create_issue_146_register_request("sip:pbx.e36:5060", auth_header_value); + let (tx, _) = create_transaction(request).await; + + let result = module.authenticate_request(&tx).await.unwrap(); + assert!( + result.is_some(), + "authentication should succeed when the digest matches the Authorization uri from issue #146" + ); +} + +#[tokio::test] +async fn test_authenticate_request_preserves_authorization_uri_transport_case() { + let server = create_issue_146_server().await; + let module = AuthModule::new(server); + let auth_header_value = r#"Digest username="111",realm="pbx.e36",nonce="K1KmT96onZZVMvBB",uri="sip:pbx.e36:5061;transport=tls",response="0c9ba3a13fbcc4f342fd7eb9c2be6a83",algorithm=MD5"#; + let request = create_issue_146_register_request("sip:pbx.e36:5061;transport=tls", auth_header_value); + let (tx, _) = create_transaction(request).await; + + let result = module.authenticate_request(&tx).await.unwrap(); + assert!( + result.is_some(), + "authentication should preserve the exact Authorization uri bytes from issue #146" + ); +} + #[tokio::test] async fn test_auth_no_credentials() { let (server, _) = create_test_server().await;