@@ -11,7 +11,7 @@ use crate::{
1111 sasl:: { SaslResponse , SaslStart } ,
1212 AuthMechanism ,
1313 } ,
14- options:: ServerApi ,
14+ options:: { ServerAddress , ServerApi } ,
1515 } ,
1616 cmap:: { Command , Connection } ,
1717 error:: { Error , Result } ,
@@ -25,6 +25,15 @@ const HUMAN_CALLBACK_TIMEOUT: Duration = Duration::from_secs(5 * 60);
2525const MACHINE_CALLBACK_TIMEOUT : Duration = Duration :: from_secs ( 60 ) ;
2626const MACHINE_INVALIDATE_SLEEP_TIMEOUT : Duration = Duration :: from_millis ( 100 ) ;
2727const API_VERSION : u32 = 1 ;
28+ const DEFAULT_ALLOWED_HOSTS : & [ & str ] = & [
29+ "*.mongodb.net" ,
30+ "*.mongodb-qa.net" ,
31+ "*.mongodb-dev.net" ,
32+ "*.mongodbgov.net" ,
33+ "localhost" ,
34+ "127.0.0.1" ,
35+ "::1" ,
36+ ] ;
2837
2938/// The user-supplied callbacks for OIDC authentication.
3039#[ derive( Clone ) ]
@@ -351,12 +360,54 @@ async fn do_two_step_auth(
351360 Ok ( ( ) )
352361}
353362
363+ fn get_allowed_hosts ( mechanism_properties : Option < & Document > ) -> Result < Vec < & str > > {
364+ if mechanism_properties. is_none ( ) {
365+ return Ok ( Vec :: from ( DEFAULT_ALLOWED_HOSTS ) ) ;
366+ }
367+ if let Some ( allowed_hosts) =
368+ mechanism_properties. and_then ( |p| p. get_array ( "ALLOWED_HOSTS" ) . ok ( ) )
369+ {
370+ return allowed_hosts
371+ . iter ( )
372+ . map ( |host| {
373+ host. as_str ( )
374+ . ok_or_else ( || auth_error ( "ALLOWED_HOSTS must contain only strings" ) )
375+ } )
376+ . collect :: < Result < Vec < _ > > > ( ) ;
377+ }
378+ Ok ( Vec :: from ( DEFAULT_ALLOWED_HOSTS ) )
379+ }
380+
381+ fn validate_address_with_allowed_hosts (
382+ mechanism_properties : Option < & Document > ,
383+ address : & ServerAddress ,
384+ ) -> Result < ( ) > {
385+ let hostname = if let ServerAddress :: Tcp { host, .. } = address {
386+ host. as_str ( )
387+ } else {
388+ return Err ( auth_error ( "OIDC human flow only supports TCP addresses" ) ) ;
389+ } ;
390+ for pattern in get_allowed_hosts ( mechanism_properties) ? {
391+ if pattern == hostname {
392+ return Ok ( ( ) ) ;
393+ }
394+ if pattern. starts_with ( "*." ) && hostname. ends_with ( & pattern[ 1 ..] ) {
395+ return Ok ( ( ) ) ;
396+ }
397+ }
398+ Err ( auth_error (
399+ "The Connection address is not in the allowed list of hosts" ,
400+ ) )
401+ }
402+
354403async fn authenticate_human (
355404 conn : & mut Connection ,
356405 credential : & Credential ,
357406 server_api : Option < & ServerApi > ,
358407 callback : Arc < CallbackInner > ,
359408) -> Result < ( ) > {
409+ validate_address_with_allowed_hosts ( credential. mechanism_properties . as_ref ( ) , & conn. address ) ?;
410+
360411 let source = credential. source . as_deref ( ) . unwrap_or ( "$external" ) ;
361412
362413 // If the access token is in the cache, we can use it to send the sasl start command and avoid
0 commit comments