From 112831c3219b2feb9343672af6b1b66cd24e22cd Mon Sep 17 00:00:00 2001
From: Dominik George <nik@naturalnet.de>
Date: Wed, 5 May 2021 23:00:54 +0200
Subject: [PATCH] [PAM] Return more specific error on OAuth2 failure

---
 Cargo.toml |  1 +
 src/pam.rs | 46 +++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 42 insertions(+), 5 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 0cbfcc3..6c57ed4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -15,6 +15,7 @@ crate-type = [ "cdylib" ]
 [dependencies]
 pamsm = { version = "^0.4.2", features = ["libpam"] }
 oauth2 = "^4.0.0"
+reqwest = "^0.11.3"
 config = "^0.11.0"
 log = "^0.4.11"
 syslog = "^5.0.0"
diff --git a/src/pam.rs b/src/pam.rs
index cb51a94..f85ab50 100644
--- a/src/pam.rs
+++ b/src/pam.rs
@@ -25,6 +25,7 @@ use oauth2::{
     AuthUrl,
     ClientId,
     ClientSecret,
+    RequestTokenError,
     ResourceOwnerPassword,
     ResourceOwnerUsername,
     Scope,
@@ -34,6 +35,7 @@ use oauth2::basic::{
     BasicClient,
     BasicTokenResponse
 };
+use oauth2::reqwest;
 use oauth2::reqwest::http_client;
 
 use pamsm::{PamServiceModule, Pam, PamFlag, PamError, PamLibExt};
@@ -71,17 +73,51 @@ fn do_legacy_auth(username: String, password: String, config: Config) -> Result<
     let scope = get_or_pam_error(&config, "pam.scope")?;
 
     let client = BasicClient::new(client_id, Some(client_secret), auth_url, Some(token_url));
-    match client
+    let result = client
         .exchange_password(
             &ResourceOwnerUsername::new(username),
             &ResourceOwnerPassword::new(password)
         )
         .add_scope(Scope::new(scope.to_string()))
-        .request(http_client) {
+        .request(http_client);
+    match result {
             Ok(t) => Ok(t),
-            Err(e) => {
-                error!("Error requesting grant: {}", e);
-                return Err(PamError::AUTHINFO_UNAVAIL);
+            Err(e) => match e {
+                RequestTokenError::Request(re) => match re {
+                    reqwest::Error::Reqwest(ree) => {
+                        if ree.is_status() {
+                            error!("Authentication failed for invalid grant: {}", ree);
+                            return Err(PamError::AUTH_ERR);
+                        } else {
+                            error!("Request error requesting token: {}", ree);
+                            return Err(PamError::AUTHINFO_UNAVAIL);
+                        }
+                    },
+                    reqwest::Error::Http(he) => {
+                        error!("HTTP error requesting token: {}", he);
+                        return Err(PamError::AUTHINFO_UNAVAIL);
+                    },
+                    reqwest::Error::Io(ioe) => {
+                        error!("IO error requesting token: {}", ioe);
+                        return Err(PamError::SYSTEM_ERR);
+                    },
+                    _ => {
+                        error!("Unknown error: {}", re);
+                        return Err(PamError::SERVICE_ERR);
+                    },
+                },
+                RequestTokenError::ServerResponse(t) => {
+                    error!("Authorization server returned error: {}", t);
+                    return Err(PamError::AUTH_ERR);
+                },
+                RequestTokenError::Parse(pe, _) => {
+                    error!("Error parsing response: {}", pe);
+                    return Err(PamError::SERVICE_ERR);
+                },
+                _ => {
+                    error!("Unknown error: {}", e);
+                    return Err(PamError::SERVICE_ERR);
+                },
             },
         }
 }
-- 
GitLab