From 15577df5f185b6db53e80f9efcb1751970445d0d Mon Sep 17 00:00:00 2001 From: Nicolas goy Date: Thu, 27 Aug 2020 23:20:25 +0200 Subject: [PATCH] Ignore root and improve logging. Fix #3. --- README.md | 18 ++++++++ nss_module/Cargo.toml | 1 + nss_module/src/lib.rs | 86 ++++++++++++++++++++++++++---------- pam_module/src/lib.rs | 100 ++++++++++++++++++++++++++++++------------ 4 files changed, 155 insertions(+), 50 deletions(-) diff --git a/README.md b/README.md index 88ed91f..b585d25 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,13 @@ like this: `-rwsr-xr-x 1 root root /usr/bin/radius_shell` +#### Su and Sudo + +`su` should work as expected with `root` password, `sudo` will ask for the +mapped user password. For example, if you log in as `fred:pass` and this user is +mapped to `admin:secret` you will be logged as `admin` but `sudo` will expect +the password of the `admin` user: `secret`. The best way to provide sudo access +to mapped user is to use the `NOPASSWD:` option. ### Auth client @@ -160,6 +167,17 @@ takes the path of the config file as command line argument. The configuration is documented in the [sample configuration file](config.toml.sample). +## Building + +If you want to build the binaries yourself, you need to be running Linux and +install rust and windows cross compiler. + +Then follow those steps: + +- Clone this repository, including submodules +- Run `make applypatch` +- Run `make release` + ## License diff --git a/nss_module/Cargo.toml b/nss_module/Cargo.toml index fbef27e..971a920 100644 --- a/nss_module/Cargo.toml +++ b/nss_module/Cargo.toml @@ -13,6 +13,7 @@ libnss = "0.3.0" paste = "1.0.0" log = "0.4.11" nss_db = {path = "../nss_db"} +nix = "0.18.0" [lib] name = "nss_radius_virtual" diff --git a/nss_module/src/lib.rs b/nss_module/src/lib.rs index 19bdaf5..556c18a 100644 --- a/nss_module/src/lib.rs +++ b/nss_module/src/lib.rs @@ -21,41 +21,77 @@ libnss_passwd_hooks!(radius_virtual, VirtualPasswd); impl PasswdHooks for VirtualPasswd { fn get_entry_by_name(name: String) -> Response { + if name == "root" { + return Response::NotFound; + } + + let root = nix::unistd::Uid::from_raw(0); + if nix::unistd::geteuid() != root { + return Response::NotFound; + } + setup_log(SYSLOG_NAME); + let config = match Config::system() { Ok(config) => config, - _ => return Response::Unavail, + Err(err) => { + error!("Cannot read system configuration: {}", err); + return Response::Unavail; + } }; + if config.debug() { + log::set_max_level(log::LevelFilter::Debug) + } + + debug!("Looking up user {}", name); + let db = match Db::with_config(&config) { Ok(db) => db, - _ => return Response::Unavail, + Err(err) => { + error!("Cannot read user database: {}", err); + return Response::Unavail; + } }; let user = db.get_user(&name).ok(); + match user { - None => Response::Success(Passwd { - name: config.mapping.default_user.username.clone(), - passwd: "x".to_string(), - uid: config.mapping.default_user.uid, - gid: config.mapping.default_user.gid, - gecos: "Radius default user".to_string(), - dir: config.mapping.default_user.home.clone(), - shell: config.mapping.default_user.shell.clone() - }), - Some(user) => Response::Success(Passwd { - name: user.mapping.username.clone(), - passwd: "x".to_string(), - uid: user.mapping.uid, - gid: user.mapping.gid, - gecos: format!( - "Mapped RADIUS account {}->{}", + None => { + debug!( + "Didn't find any user for {}, \ + fallling back to default user {}", + name, config.mapping.default_user.username + ); + Response::Success(Passwd { + name: config.mapping.default_user.username.clone(), + passwd: "x".to_string(), + uid: config.mapping.default_user.uid, + gid: config.mapping.default_user.gid, + gecos: "Radius default user".to_string(), + dir: config.mapping.default_user.home.clone(), + shell: config.mapping.default_user.shell.clone(), + }) + } + Some(user) => { + debug!( + "Found user {}, mapped to {}", name, user.mapping.username - ) - .to_string(), - dir: user.mapping.home, - shell: config.mapping.default_user.shell.clone() - }), + ); + Response::Success(Passwd { + name: user.mapping.username.clone(), + passwd: "x".to_string(), + uid: user.mapping.uid, + gid: user.mapping.gid, + gecos: format!( + "Mapped RADIUS account {}->{}", + name, user.mapping.username + ) + .to_string(), + dir: user.mapping.home, + shell: config.mapping.default_user.shell.clone(), + }) + } } } fn get_all_entries() -> Response> { @@ -71,6 +107,10 @@ libnss_shadow_hooks!(radius_virtual, VirtualShadow); impl ShadowHooks for VirtualShadow { fn get_entry_by_name(name: String) -> Response { + if name == "root" { + return Response::NotFound; + } + let config = match Config::system() { Ok(config) => config, _ => return Response::Unavail, diff --git a/pam_module/src/lib.rs b/pam_module/src/lib.rs index fbfc3b7..31cd38e 100644 --- a/pam_module/src/lib.rs +++ b/pam_module/src/lib.rs @@ -13,12 +13,11 @@ use radius::Client; use radius::Credentials; use radius::Error; - const SYSLOG_NAME: &str = "pam_radius_virtual"; -struct PamTime; +struct PamRadius; -impl PamServiceModule for PamTime { +impl PamServiceModule for PamRadius { fn authenticate( pamh: Pam, _flags: PamFlag, @@ -26,47 +25,78 @@ impl PamServiceModule for PamTime { ) -> PamError { setup_log(SYSLOG_NAME); + let config = match Config::system() { + Ok(config) => config, + Err(err) => { + error!("Cannot read configuration file: {}", err); + return PamError::SERVICE_ERR; + } + }; + + if config.debug() { + log::set_max_level(log::LevelFilter::Debug) + } + let username = match pamh.get_user(None) { Ok(Some(u)) => u, - Ok(None) => return PamError::USER_UNKNOWN, + Ok(None) => { + error!("Cannot get username"); + return PamError::USER_UNKNOWN; + } Err(e) => return e, }; let username = match username.to_str() { Ok(u) => u, - _ => return PamError::USER_UNKNOWN, + _ => { + error!("Cannot convert username to string"); + return PamError::USER_UNKNOWN; + } }; + debug!("Got username {}", username); + + if username == "root" { + return PamError::USER_UNKNOWN; + } + let pass = match pamh.get_authtok(None) { Ok(Some(p)) => p, - Ok(None) => return PamError::AUTH_ERR, + Ok(None) => { + error!("Cannot get password"); + return PamError::AUTH_ERR; + } Err(e) => return e, }; let pass = match pass.to_str() { Ok(p) => p, - _ => return PamError::AUTH_ERR, + _ => { + error!("Cannot convert password to string"); + return PamError::AUTH_ERR; + } }; - let config = Config::system(); - - let config = match config { - Ok(config) => config, - _ => return PamError::SERVICE_ERR, - }; + debug!("Got password for {}", username); let client = Client::with_config(&config.radius); let client = match client { Ok(client) => client, - _ => return PamError::SERVICE_ERR, + Err(err) => { + error!("Cannot create radius client: {}", err); + return PamError::SERVICE_ERR; + } }; let db = Db::with_config(&config); let mut db = match db { Ok(db) => db, - _ => return PamError::SERVICE_ERR, + Err(err) => { + error!("Cannot read database: {}", err); + return PamError::SERVICE_ERR; + } }; let cred = Credentials::with_username_password(username, pass); @@ -76,7 +106,10 @@ impl PamServiceModule for PamTime { let radius_user = match res { Ok(user) => user, Err(Error::AuthReject) => return PamError::AUTH_ERR, - _ => return PamError::SERVICE_ERR, + Err(err) => { + error!("Radius error: {}", err); + return PamError::SERVICE_ERR; + } }; let res = config.map_user(&radius_user); @@ -88,26 +121,30 @@ impl PamServiceModule for PamTime { let res = db.store_user(&user); - let cookie = match res { Ok(s) => s, - _ => return PamError::SERVICE_ERR, + Err(err) => { + error!("Cannot write to database: {}", err); + return PamError::SERVICE_ERR; + } }; let res = pamh.putenv(&format!("RADIUS_USER={}", username)); - if let Err(_) = res { + if let Err(_) = res { return PamError::SERVICE_ERR; } let res = pamh.putenv(&format!("RADIUS_USER_COOKIE={}", cookie)); - if let Err(_) = res { + if let Err(_) = res { return PamError::SERVICE_ERR; } - match res { Ok(_) => return PamError::SUCCESS, - _ => return PamError::SERVICE_ERR, + Err(err) => { + error!("Cannot set environment variables: {}", err); + return PamError::SERVICE_ERR; + } }; } @@ -118,15 +155,20 @@ impl PamServiceModule for PamTime { let config = match config { Ok(config) => config, - _ => return PamError::SERVICE_ERR, + Err(err) => { + error!("Cannot read configuration file: {}", err); + return PamError::SERVICE_ERR; + } }; - let db = Db::with_config(&config); let db = match db { Ok(db) => db, - _ => return PamError::SERVICE_ERR, + Err(err) => { + error!("Cannot read database: {}", err); + return PamError::SERVICE_ERR; + } }; let user = match pamh.get_user(None) { @@ -140,13 +182,17 @@ impl PamServiceModule for PamTime { _ => return PamError::USER_UNKNOWN, }; + if user == "root" { + return PamError::USER_UNKNOWN; + } + let user = db.get_user(user); match user { Ok(_user) => PamError::SUCCESS, - _ => PamError::AUTH_ERR + _ => PamError::AUTH_ERR, } } } -pam_module!(PamTime); +pam_module!(PamRadius);