diff --git a/.editorconfig b/.editorconfig
index b458585c45b727516d9c38ac8adb526cc786b5ad..81fd7bbc0a20631035ebd836e6787fa4e76f9cb4 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -6,6 +6,6 @@ end_of_line = lf
 insert_final_newline = true
 trim_trailing_whitespace = true
 
-[*.py]
+[*.rs]
 indent_style = space
 indent_size = 4
diff --git a/.gitignore b/.gitignore
index 2146c76c4519ab6ab4923d0ca2e98ce95f0afc91..0fcc468a8751390f65545ce6c4fbe1350db0428d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,139 +1,14 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-parts/
-sdist/
-var/
-wheels/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-#  Usually these files are written by a python script from a template
-#  before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
+# Generated by Cargo
+# will have compiled files and executables
+debug/
 target/
 
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-#   For a library or package, you might want to ignore these files since the code is
-#   intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-#   However, in case of collaboration, if having platform-specific dependencies or dependencies
-#   having no cross-platform support, pipenv may install dependencies that don't work, or not
-#   install all needed dependencies.
-#Pipfile.lock
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
 
-# Cython debug symbols
-cython_debug/
+# These are backup files generated by rustfmt
+**/*.rs.bk
 
 # Editor files
 *~
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000000000000000000000000000000000000..0553594fce6015fffe3332355e9cb08b001ccb2f
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "nss_pam_oidc"
+version = "0.1.0"
+authors = ["Dominik George <nik@naturalnet.de>"]
+edition = "2018"
+description = "NSS/PAM modules for OpenID Connect/OAuth2"
+repository = "https://edugit.org/debian-edu-ng/nss-pam-oidc"
+license = "Apache-2.0"
+categories = ["authentication", "os", "os::linux-apis"]
+
+[lib]
+name = "nss_pam_oidc"
+crate-type = [ "cdylib" ]
+
+[dependencies]
+pamsm = { version = "^0.4.2", features = ["libpam"] }
+oauth2 = "^4.0.0"
+config = "^0.11.0"
+
+[profile.release]
+opt-level = 'z'
+lto = true
diff --git a/lib/security/nss_pam_oidc.py b/lib/security/nss_pam_oidc.py
deleted file mode 100644
index c52dee6c9ef9a6f04abf8efee285a84c8f7d96e5..0000000000000000000000000000000000000000
--- a/lib/security/nss_pam_oidc.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Copyright 2021 Dominik George <nik@naturalnet.de>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from nss_pam_oidc.pam import (
-    pam_sm_authenticate,
-    pam_sm_setcred,
-    pam_sm_acct_mgmt,
-    pam_sm_open_session,
-    pam_sm_close_session,
-    pam_sm_chauthtok,
-)
diff --git a/nss_pam_oidc/__init__.py b/nss_pam_oidc/__init__.py
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/nss_pam_oidc/config.py b/nss_pam_oidc/config.py
deleted file mode 100644
index 62f12b198986111d608f2a1e622be3ada34e931c..0000000000000000000000000000000000000000
--- a/nss_pam_oidc/config.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright 2021 Dominik George <nik@naturalnet.de>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-import os
-
-import toml
-
-DEFAULT_CONFIG_FILE = "/etc/nss_pam_oidc.toml"
-DEFAULT_CONFIG = {
-    "pam": {
-        "flow": "password",
-    },
-    "nss": {},
-}
-
-
-def load_config(config_file: str) -> dict[str, dict[str, str]]:
-    """Load full configuration, amended wit hdefault config."""
-    config = {}
-
-    # First, copy default configuration
-    for name, conf in DEFAULT_CONFIG.items():
-        config[name] = conf.copy()
-
-    # Second, load configuration from file
-    if os.path.exists(config_file):
-        config_toml = toml.load(config_file)
-        for name, conf in config.items():
-            conf.update(config_toml.get(name, {}))
-
-    return config
-
-
-def get_config(section: str, **kwargs) -> dict[str, str]:
-    """Get configuration for a section, layered from defaults, config file, and args."""
-    config_file = kwargs.pop("config", DEFAULT_CONFIG_FILE)
-    config = load_config(config_file)
-
-    # Lastly, override with passed arguments
-    config[section].update(kwargs)
-
-    return config[section]
-
-
-def filter_config(config: dict[str, str], keys: list[str]) -> dict[str, str]:
-    """Return a copy of the config dictionary with only the requested keys."""
-    return {k: v for k, v in config.items() if k in keys}
diff --git a/nss_pam_oidc/pam.py b/nss_pam_oidc/pam.py
deleted file mode 100644
index 4353e6eb406080081d6b4bc03538b8456836fa50..0000000000000000000000000000000000000000
--- a/nss_pam_oidc/pam.py
+++ /dev/null
@@ -1,90 +0,0 @@
-# Copyright 2021 Dominik George <nik@naturalnet.de>
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#     http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from oauthlib.oauth2 import InvalidGrantError, LegacyApplicationClient
-from requests.exceptions import RequestException
-from requests_oauthlib import OAuth2Session
-
-from .config import filter_config, get_config
-
-
-def _split_argv(argv: list[str]) -> dict[str, str]:
-    args = {}
-    for arg in argv:
-        if "=" in arg:
-            name, val = arg.split("=", 1)
-        else:
-            name, val = arg, True
-        args[name] = val
-    return args
-
-
-def _do_legacy_auth(username: str, password: str, config: dict[str, str]):
-    client_config = filter_config(config, ["client_id"])
-    session_config = filter_config(config, ["client_id"])
-    fetch_config = filter_config(config, ["client_id", "client_secret", "token_url"])
-
-    client = LegacyApplicationClient(**client_config)
-    session = OAuth2Session(client=client, **session_config)
-
-    token = session.fetch_token(username=username, password=password, **fetch_config)
-    return token
-
-
-def pam_sm_authenticate(pamh, flags, argv):
-    args = _split_argv(argv)
-    config = get_config("pam", args)
-
-    if config.pop("flow") == "password":
-        try:
-            user = pamh.get_user(None)
-            password = pamh.authtok
-        except pamh.exception as e:
-            return e.pam_result
-        if user is None or password is None:
-            return pamh.PAM_CRED_INSUFFICIENT
-
-        try:
-            token = _do_legacy_auth(username, password)
-        except InvalidGrantError:
-            return pamh.PAM_AUTH_ERROR
-        except RequestException:
-            return pamh.PAM_AUTHINFO_UNAVAIL
-        except:
-            return pamh.PAM_SERVICE_ERR
-
-    if "access_token" in token:
-        return pamh.PAM_SUCCESS
-
-    return pamh.PAM_AUTH_ERR
-
-
-def pam_sm_setcred(pamh, flags, argv):
-    return pamh.PAM_SUCCESS
-
-
-def pam_sm_acct_mgmt(pamh, flags, argv):
-    return pamh.PAM_SUCCESS
-
-
-def pam_sm_open_session(pamh, flags, argv):
-    return pamh.PAM_SUCCESS
-
-
-def pam_sm_close_session(pamh, flags, argv):
-    return pamh.PAM_SUCCESS
-
-
-def pam_sm_chauthtok(pamh, flags, argv):
-    return pamh.PAM_SUCCESS
diff --git a/poetry.lock b/poetry.lock
deleted file mode 100644
index bc39f6bc252c57cae23dfb41a68b448067f6bc1c..0000000000000000000000000000000000000000
--- a/poetry.lock
+++ /dev/null
@@ -1,130 +0,0 @@
-[[package]]
-name = "certifi"
-version = "2020.12.5"
-description = "Python package for providing Mozilla's CA Bundle."
-category = "main"
-optional = false
-python-versions = "*"
-
-[[package]]
-name = "chardet"
-version = "4.0.0"
-description = "Universal encoding detector for Python 2 and 3"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[[package]]
-name = "idna"
-version = "2.10"
-description = "Internationalized Domain Names in Applications (IDNA)"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[[package]]
-name = "oauthlib"
-version = "3.1.0"
-description = "A generic, spec-compliant, thorough implementation of the OAuth request-signing logic"
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.extras]
-rsa = ["cryptography"]
-signals = ["blinker"]
-signedtoken = ["cryptography", "pyjwt (>=1.0.0)"]
-
-[[package]]
-name = "requests"
-version = "2.25.1"
-description = "Python HTTP for Humans."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
-
-[package.dependencies]
-certifi = ">=2017.4.17"
-chardet = ">=3.0.2,<5"
-idna = ">=2.5,<3"
-urllib3 = ">=1.21.1,<1.27"
-
-[package.extras]
-security = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)"]
-socks = ["PySocks (>=1.5.6,!=1.5.7)", "win-inet-pton"]
-
-[[package]]
-name = "requests-oauthlib"
-version = "1.3.0"
-description = "OAuthlib authentication support for Requests."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*"
-
-[package.dependencies]
-oauthlib = ">=3.0.0"
-requests = ">=2.0.0"
-
-[package.extras]
-rsa = ["oauthlib[signedtoken] (>=3.0.0)"]
-
-[[package]]
-name = "toml"
-version = "0.10.2"
-description = "Python Library for Tom's Obvious, Minimal Language"
-category = "main"
-optional = false
-python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*"
-
-[[package]]
-name = "urllib3"
-version = "1.26.4"
-description = "HTTP library with thread-safe connection pooling, file post, and more."
-category = "main"
-optional = false
-python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, <4"
-
-[package.extras]
-secure = ["pyOpenSSL (>=0.14)", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "certifi", "ipaddress"]
-socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
-brotli = ["brotlipy (>=0.6.0)"]
-
-[metadata]
-lock-version = "1.1"
-python-versions = "^3.9"
-content-hash = "f70d776e9a6a2676f9c1816a08a35616c61a5a9af75631200da033c3afd4cb4e"
-
-[metadata.files]
-certifi = [
-    {file = "certifi-2020.12.5-py2.py3-none-any.whl", hash = "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830"},
-    {file = "certifi-2020.12.5.tar.gz", hash = "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c"},
-]
-chardet = [
-    {file = "chardet-4.0.0-py2.py3-none-any.whl", hash = "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5"},
-    {file = "chardet-4.0.0.tar.gz", hash = "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa"},
-]
-idna = [
-    {file = "idna-2.10-py2.py3-none-any.whl", hash = "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0"},
-    {file = "idna-2.10.tar.gz", hash = "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6"},
-]
-oauthlib = [
-    {file = "oauthlib-3.1.0-py2.py3-none-any.whl", hash = "sha256:df884cd6cbe20e32633f1db1072e9356f53638e4361bef4e8b03c9127c9328ea"},
-    {file = "oauthlib-3.1.0.tar.gz", hash = "sha256:bee41cc35fcca6e988463cacc3bcb8a96224f470ca547e697b604cc697b2f889"},
-]
-requests = [
-    {file = "requests-2.25.1-py2.py3-none-any.whl", hash = "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e"},
-    {file = "requests-2.25.1.tar.gz", hash = "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804"},
-]
-requests-oauthlib = [
-    {file = "requests-oauthlib-1.3.0.tar.gz", hash = "sha256:b4261601a71fd721a8bd6d7aa1cc1d6a8a93b4a9f5e96626f8e4d91e8beeaa6a"},
-    {file = "requests_oauthlib-1.3.0-py2.py3-none-any.whl", hash = "sha256:7f71572defaecd16372f9006f33c2ec8c077c3cfa6f5911a9a90202beb513f3d"},
-    {file = "requests_oauthlib-1.3.0-py3.7.egg", hash = "sha256:fa6c47b933f01060936d87ae9327fead68768b69c6c9ea2109c48be30f2d4dbc"},
-]
-toml = [
-    {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
-    {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
-]
-urllib3 = [
-    {file = "urllib3-1.26.4-py2.py3-none-any.whl", hash = "sha256:2f4da4594db7e1e110a944bb1b551fdf4e6c136ad42e4234131391e21eb5b0df"},
-    {file = "urllib3-1.26.4.tar.gz", hash = "sha256:e7b021f7241115872f92f43c6508082facffbd1c048e3c6e2bb9c2a157e28937"},
-]
diff --git a/pyproject.toml b/pyproject.toml
deleted file mode 100644
index b97730d6b35f11d46f0572bea95f3a53e9c2910e..0000000000000000000000000000000000000000
--- a/pyproject.toml
+++ /dev/null
@@ -1,24 +0,0 @@
-[tool.poetry]
-name = "nss-pam-oidc"
-version = "0.1.0"
-description = "NSS/PAM modules for OpenID Connect/OAuth2"
-authors = ["Dominik George <nik@naturalnet.de>"]
-license = "Apache-2.0"
-readme = "README.rst"
-repository = "https://edugit.org/debian-edu-ng/nss-pam-oidc"
-classifiers = [
-    "Environment :: Console",
-    "Intended Audience :: System Administrators",
-]
-
-[tool.poetry.dependencies]
-python = "^3.9"
-toml = "^0.10.2"
-oauthlib = "^3.1.0"
-requests-oauthlib = "^1.3.0"
-
-[tool.poetry.dev-dependencies]
-
-[build-system]
-requires = ["poetry-core>=1.0.0"]
-build-backend = "poetry.core.masonry.api"
diff --git a/src/config.rs b/src/config.rs
new file mode 100644
index 0000000000000000000000000000000000000000..66cf6b2f07f93d91061a797ad3c3b49e14b1a6a0
--- /dev/null
+++ b/src/config.rs
@@ -0,0 +1,57 @@
+/* Copyright 2021 Dominik George <nik@naturalnet.de>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+extern crate config;
+
+fn load_config(config_file: String) -> config::Config {
+    let mut conf = config::Config::default();
+
+    conf.set("pam.flow", "password");
+
+    conf
+        .merge(config::File::with_name(&config_file)).unwrap()
+        .merge(config::Environment::with_prefix("NSS_PAM_OIDC")).unwrap();
+
+    return conf;
+}
+
+pub fn get_config(conf_args: config::Config) -> config::Config {
+    let config_file: String;
+    let config_file_passed = conf_args.get_str("config");
+    if config_file_passed.is_ok() {
+        config_file = config_file_passed.unwrap().to_string();
+    } else {
+        config_file = "/etc/nss_pam_oidc".to_string();
+    }
+
+    let mut conf = load_config(config_file);
+    conf.merge(conf_args).unwrap();
+
+    return conf;
+}
+
+pub fn argv_to_config(argv: Vec<String>) -> config::Config {
+    let mut conf = config::Config::default();
+
+    for arg in &argv {
+        if arg.contains("=") {
+            let split: Vec<&str> = arg.split("=").collect();
+            conf.set(split[0], split[1]);
+        } else {
+            conf.set(arg, true);
+        }
+    }
+    return conf;
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000000000000000000000000000000000000..1762c0e8764bb393213b3768064678e793b766d8
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,2 @@
+mod config;
+mod pam;
diff --git a/src/pam.rs b/src/pam.rs
new file mode 100644
index 0000000000000000000000000000000000000000..d2e9aab38f07d00d2dcc15aa1793a8ca365495fe
--- /dev/null
+++ b/src/pam.rs
@@ -0,0 +1,91 @@
+use crate::config::{
+    argv_to_config,
+    get_config
+};
+use config::Config;
+
+use oauth2::{
+    AuthUrl,
+    ClientId,
+    ClientSecret,
+    ResourceOwnerPassword,
+    ResourceOwnerUsername,
+    Scope,
+    TokenUrl
+};
+use oauth2::basic::{
+    BasicClient,
+    BasicTokenResponse
+};
+use oauth2::reqwest::http_client;
+
+extern crate pamsm;
+use pamsm::{PamServiceModule, Pam, PamFlag, PamError, PamLibExt};
+
+fn get_or_pam_error(config: &Config, key: &str) -> Result<String, PamError> {
+    match config.get_str(key) {
+        Ok(v) => Ok(v),
+        _ => Err(PamError::SERVICE_ERR),
+    }
+}
+
+fn do_legacy_auth(username: String, password: String, config: Config) -> Result<BasicTokenResponse, PamError> {
+    let client_id = ClientId::new(get_or_pam_error(&config, "pam.client_id")?);
+    let client_secret = ClientSecret::new(get_or_pam_error(&config, "pam.client_secret")?);
+    let auth_url = match AuthUrl::new(get_or_pam_error(&config, "pam.auth_url")?) {
+        Ok(u) => u,
+        _ => return Err(PamError::SERVICE_ERR),
+    };
+    let token_url = match TokenUrl::new(get_or_pam_error(&config, "pam.token_url")?){
+        Ok(u) => u,
+        _ => return Err(PamError::SERVICE_ERR),
+    };
+    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
+        .exchange_password(
+            &ResourceOwnerUsername::new(username),
+            &ResourceOwnerPassword::new(password)
+        )
+        .add_scope(Scope::new(scope.to_string()))
+        .request(http_client) {
+            Ok(t) => Ok(t),
+            _ => Err(PamError::AUTHINFO_UNAVAIL),
+        }
+}
+
+struct PamOidc;
+
+impl PamServiceModule for PamOidc {
+    fn authenticate(pamh: Pam, _: PamFlag, argv: Vec<String>) -> PamError {
+        let conf_args = argv_to_config(argv);
+        let conf = get_config(conf_args);
+
+        if conf.get_str("pam.flow").unwrap() == "password" {
+            let username = match pamh.get_user(None) {
+                Ok(Some(u)) => match u.to_str() {
+                    Ok(u) => u,
+                    _ => return PamError::CRED_INSUFFICIENT,
+                },
+                Ok(None) => return PamError::CRED_INSUFFICIENT,
+                Err(e) => return e,
+            };
+            let password = match pamh.get_authtok(None) {
+                Ok(Some(p)) => match p.to_str() {
+                    Ok(p) => p,
+                    _ => return PamError::CRED_INSUFFICIENT,
+                },
+                Ok(None) => return PamError::CRED_INSUFFICIENT,
+                Err(e) => return e,
+            };
+
+            match do_legacy_auth(username.to_string(), password.to_string(), conf) {
+                Ok(_) => return PamError::SUCCESS,
+                Err(e) => return e,
+            };
+        }
+
+        return PamError::SERVICE_ERR;
+    }
+}