feat: clone from: https://github.com/anowell/pam-rs
This commit is contained in:
2
Cargo.toml
Normal file
2
Cargo.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[workspace]
|
||||||
|
members = ["pam", "pam-sober", "pam-http"]
|
||||||
22
LICENSE
Normal file
22
LICENSE
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015 TOZNY
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
|
|
||||||
25
README.md
25
README.md
@@ -1,2 +1,25 @@
|
|||||||
# pam-rs
|
pam-rs
|
||||||
|
========
|
||||||
|
|
||||||
|
Rust interface to the pluggable authentication module framework (PAM).
|
||||||
|
|
||||||
|
The goal of this library is to provide a type-safe API that can be used to
|
||||||
|
interact with PAM. The library is incomplete - currently it supports a subset
|
||||||
|
of functions for use in a pam authentication module. A pam module is a shared
|
||||||
|
library that is invoked to authenticate a user, or to perform other functions.
|
||||||
|
|
||||||
|
## 🌐 [pam-http](pam-http)
|
||||||
|
|
||||||
|
An example of using pam-rs by performing HTTP basic access auth to authenticate users.
|
||||||
|
|
||||||
|
## 🍻 [pam-sober](pam-sober)
|
||||||
|
|
||||||
|
If you aren't sober enough for basic math, you can't login!
|
||||||
|
|
||||||
|
### Credits
|
||||||
|
|
||||||
|
The contents of this repo are heavily borrowed from:
|
||||||
|
|
||||||
|
- [tozny/rust-pam](https://github.com/tozny/rust-pam)
|
||||||
|
- [ndenev/pam_groupmap](https://github.com/ndenev/pam_groupmap)
|
||||||
|
- [beatgammit/pam-http](https://github.com/beatgammit/pam-http)
|
||||||
|
|||||||
12
pam-http/Cargo.toml
Normal file
12
pam-http/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "pam-http"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Anthony Nowell <anowell@gmail.com>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "pam_http"
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pam-bindings = { path = "../pam/" }
|
||||||
|
reqwest = { version = "0.11.3", features = ["blocking"] }
|
||||||
12
pam-http/Justfile
Normal file
12
pam-http/Justfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
all:
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
install:
|
||||||
|
@cargo build --release
|
||||||
|
sudo cp conf/http-auth /etc/pam.d/
|
||||||
|
sudo cp ../target/release/libpam_http.so /lib/security/pam_http.so
|
||||||
|
|
||||||
|
test:
|
||||||
|
@just install
|
||||||
|
gcc -o ../target/pam_test test.c -lpam -lpam_misc
|
||||||
35
pam-http/README.md
Normal file
35
pam-http/README.md
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
pam-http
|
||||||
|
========
|
||||||
|
|
||||||
|
A PAM HTTP BasicAuth module built using pam-rs
|
||||||
|
|
||||||
|
# Prerequisites
|
||||||
|
|
||||||
|
You need some libraries before you build like libpam and libssl.
|
||||||
|
|
||||||
|
If you're going to build on Ubuntu, just run this:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo apt-get install -y build-essential libpam0g-dev libpam0g libssl-dev
|
||||||
|
```
|
||||||
|
|
||||||
|
# Building
|
||||||
|
|
||||||
|
Just use `cargo build`.
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
You need to move the build product to a folder where PAM is looking for modules.
|
||||||
|
|
||||||
|
If you're using Ubuntu you can move `libpam_http.so` to `/lib/security`.
|
||||||
|
After doing so you need to make sure it has proper permissions: `sudo chmod 755 /lib/security/libpam_http.so`.
|
||||||
|
Then you can place a configuration file in `/etc/pam.d/`. It can look something like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
auth sufficient libpam_http.so url=https://theserver.example.com/someendpoint
|
||||||
|
account sufficient libpam_http.so
|
||||||
|
```
|
||||||
|
|
||||||
|
Make sure the endpoint you're specifying can receive GET requests and supports
|
||||||
|
[HTTP Basic Authentication](https://en.wikipedia.org/wiki/Basic_access_authentication#Client_side).
|
||||||
|
If the user is authenticated successfully it should return HTTP 200.
|
||||||
2
pam-http/conf/http-auth
Normal file
2
pam-http/conf/http-auth
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
auth sufficient pam_http.so url=http://localhost:3000
|
||||||
|
account sufficient pam_http.so
|
||||||
88
pam-http/src/lib.rs
Normal file
88
pam-http/src/lib.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
extern crate pam;
|
||||||
|
extern crate reqwest;
|
||||||
|
|
||||||
|
use pam::constants::{PamFlag, PamResultCode, PAM_PROMPT_ECHO_OFF};
|
||||||
|
use pam::conv::Conv;
|
||||||
|
use pam::module::{PamHandle, PamHooks};
|
||||||
|
use reqwest::blocking::Client;
|
||||||
|
use reqwest::StatusCode;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::time::Duration;
|
||||||
|
use pam::pam_try;
|
||||||
|
|
||||||
|
struct PamHttp;
|
||||||
|
pam::pam_hooks!(PamHttp);
|
||||||
|
|
||||||
|
impl PamHooks for PamHttp {
|
||||||
|
// This function performs the task of authenticating the user.
|
||||||
|
fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
||||||
|
println!("Let's auth over HTTP");
|
||||||
|
|
||||||
|
let args: Vec<_> = args
|
||||||
|
.iter()
|
||||||
|
.map(|s| s.to_string_lossy())
|
||||||
|
.collect();
|
||||||
|
let args: HashMap<&str, &str> = args
|
||||||
|
.iter()
|
||||||
|
.map(|s| {
|
||||||
|
let mut parts = s.splitn(2, '=');
|
||||||
|
(parts.next().unwrap(), parts.next().unwrap_or(""))
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
let user = pam_try!(pamh.get_user(None));
|
||||||
|
|
||||||
|
let url: &str = match args.get("url") {
|
||||||
|
Some(url) => url,
|
||||||
|
None => return PamResultCode::PAM_AUTH_ERR,
|
||||||
|
};
|
||||||
|
|
||||||
|
let conv = match pamh.get_item::<Conv>() {
|
||||||
|
Ok(Some(conv)) => conv,
|
||||||
|
Ok(None) => {
|
||||||
|
unreachable!("No conv available");
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
println!("Couldn't get pam_conv");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let password = pam_try!(conv.send(PAM_PROMPT_ECHO_OFF, "Word, yo: "));
|
||||||
|
let password = match password {
|
||||||
|
Some(password) => Some(pam_try!(password.to_str(), PamResultCode::PAM_AUTH_ERR)),
|
||||||
|
None => None,
|
||||||
|
};
|
||||||
|
println!("Got a password {:?}", password);
|
||||||
|
let status = pam_try!(
|
||||||
|
get_url(url, &user, password),
|
||||||
|
PamResultCode::PAM_AUTH_ERR
|
||||||
|
);
|
||||||
|
|
||||||
|
if !status.is_success() {
|
||||||
|
println!("HTTP Error: {}", status);
|
||||||
|
return PamResultCode::PAM_AUTH_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
PamResultCode::PAM_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sm_setcred(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
||||||
|
println!("set credentials");
|
||||||
|
PamResultCode::PAM_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acct_mgmt(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
||||||
|
println!("account management");
|
||||||
|
PamResultCode::PAM_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_url(url: &str, user: &str, password: Option<&str>) -> reqwest::Result<StatusCode> {
|
||||||
|
let client = Client::builder().timeout(Duration::from_secs(15)).build()?;
|
||||||
|
client
|
||||||
|
.get(url)
|
||||||
|
.basic_auth(user, password)
|
||||||
|
.send()
|
||||||
|
.map(|r| r.status())
|
||||||
|
}
|
||||||
52
pam-http/test.c
Normal file
52
pam-http/test.c
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <security/pam_misc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
const struct pam_conv conv = {
|
||||||
|
misc_conv,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
pam_handle_t* pamh = NULL;
|
||||||
|
int retval;
|
||||||
|
const char* user = "nobody";
|
||||||
|
|
||||||
|
if(argc != 2) {
|
||||||
|
printf("Usage: app [username]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
user = argv[1];
|
||||||
|
|
||||||
|
retval = pam_start("http-auth", user, &conv, &pamh);
|
||||||
|
|
||||||
|
// Are the credentials correct?
|
||||||
|
if (retval == PAM_SUCCESS) {
|
||||||
|
printf("Credentials accepted.\n");
|
||||||
|
retval = pam_authenticate(pamh, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can the accound be used at this time?
|
||||||
|
if (retval == PAM_SUCCESS) {
|
||||||
|
printf("Account is valid.\n");
|
||||||
|
retval = pam_acct_mgmt(pamh, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did everything work?
|
||||||
|
if (retval == PAM_SUCCESS) {
|
||||||
|
printf("Authenticated\n");
|
||||||
|
} else {
|
||||||
|
printf("Not Authenticated\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// close PAM (end session)
|
||||||
|
if (pam_end(pamh, retval) != PAM_SUCCESS) {
|
||||||
|
pamh = NULL;
|
||||||
|
printf("check_user: failed to release authenticator\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval == PAM_SUCCESS ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
12
pam-sober/Cargo.toml
Normal file
12
pam-sober/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[package]
|
||||||
|
name = "pam-sober"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Anthony Nowell <anowell@gmail.com>"]
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "pam_sober"
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
pam-bindings = { path = "../pam/" }
|
||||||
|
rand = "0.8.4"
|
||||||
12
pam-sober/Justfile
Normal file
12
pam-sober/Justfile
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
all:
|
||||||
|
cargo build
|
||||||
|
|
||||||
|
install:
|
||||||
|
@cargo build --release
|
||||||
|
sudo cp conf/sober-auth /etc/pam.d/
|
||||||
|
sudo cp ../target/release/libpam_sober.so /lib/security/pam_sober.so
|
||||||
|
|
||||||
|
test:
|
||||||
|
@just install
|
||||||
|
gcc -o ../target/pam_test test.c -lpam -lpam_misc
|
||||||
2
pam-sober/conf/sober-auth
Normal file
2
pam-sober/conf/sober-auth
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
auth sufficient pam_sober.so
|
||||||
|
account sufficient pam_sober.so
|
||||||
73
pam-sober/src/lib.rs
Normal file
73
pam-sober/src/lib.rs
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
extern crate pam;
|
||||||
|
extern crate rand;
|
||||||
|
|
||||||
|
use pam::constants::{PamFlag, PamResultCode, PAM_PROMPT_ECHO_ON};
|
||||||
|
use pam::conv::Conv;
|
||||||
|
use pam::module::{PamHandle, PamHooks};
|
||||||
|
use rand::Rng;
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use pam::pam_try;
|
||||||
|
|
||||||
|
struct PamSober;
|
||||||
|
pam::pam_hooks!(PamSober);
|
||||||
|
|
||||||
|
impl PamHooks for PamSober {
|
||||||
|
// This function performs the task of authenticating the user.
|
||||||
|
fn sm_authenticate(pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
||||||
|
println!("Let's make sure you're sober enough to perform basic addition");
|
||||||
|
|
||||||
|
/* TODO: use args to change difficulty ;-)
|
||||||
|
let args: HashMap<&str, &str> = args.iter().map(|s| {
|
||||||
|
let mut parts = s.splitn(2, "=");
|
||||||
|
(parts.next().unwrap(), parts.next().unwrap_or(""))
|
||||||
|
}).collect();
|
||||||
|
*/
|
||||||
|
|
||||||
|
// TODO: maybe we can change difficulty base on user?
|
||||||
|
// let user = pam_try!(pam.get_user(None));
|
||||||
|
|
||||||
|
let conv = match pamh.get_item::<Conv>() {
|
||||||
|
Ok(Some(conv)) => conv,
|
||||||
|
Ok(None) => todo!(),
|
||||||
|
Err(err) => {
|
||||||
|
println!("Couldn't get pam_conv");
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut rng = rand::thread_rng();
|
||||||
|
let a = rng.gen::<u32>() % 100;
|
||||||
|
let b = rng.gen::<u32>() % 100;
|
||||||
|
let math = format!("{} + {} = ", a, b);
|
||||||
|
|
||||||
|
// This println kinda helps debugging since the test script doesn't echo
|
||||||
|
eprintln!("[DEBUG]: {}{}", math, a + b);
|
||||||
|
|
||||||
|
let password = pam_try!(conv.send(PAM_PROMPT_ECHO_ON, &math));
|
||||||
|
|
||||||
|
if let Some(password) = password {
|
||||||
|
let password = pam_try!(password.to_str(), PamResultCode::PAM_AUTH_ERR);
|
||||||
|
let answer = pam_try!(u32::from_str(password), PamResultCode::PAM_AUTH_ERR);
|
||||||
|
if answer == a + b {
|
||||||
|
PamResultCode::PAM_SUCCESS
|
||||||
|
} else {
|
||||||
|
println!("Wrong answer provided {} + {} != {}", a, b, answer);
|
||||||
|
PamResultCode::PAM_AUTH_ERR
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
println!("You failed the PAM sobriety test.");
|
||||||
|
PamResultCode::PAM_AUTH_ERR
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sm_setcred(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
||||||
|
println!("set credentials");
|
||||||
|
PamResultCode::PAM_SUCCESS
|
||||||
|
}
|
||||||
|
|
||||||
|
fn acct_mgmt(_pamh: &mut PamHandle, _args: Vec<&CStr>, _flags: PamFlag) -> PamResultCode {
|
||||||
|
println!("account management");
|
||||||
|
PamResultCode::PAM_SUCCESS
|
||||||
|
}
|
||||||
|
}
|
||||||
53
pam-sober/test.c
Normal file
53
pam-sober/test.c
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <security/pam_misc.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
const struct pam_conv conv = {
|
||||||
|
misc_conv,
|
||||||
|
NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
int main(int argc, char *argv[]) {
|
||||||
|
pam_handle_t* pamh = NULL;
|
||||||
|
int retval;
|
||||||
|
const char* user = "nobody";
|
||||||
|
|
||||||
|
if(argc != 2) {
|
||||||
|
printf("Usage: app [username]\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
user = argv[1];
|
||||||
|
|
||||||
|
retval = pam_start("sober-auth", user, &conv, &pamh);
|
||||||
|
|
||||||
|
// Are the credentials correct?
|
||||||
|
if (retval == PAM_SUCCESS) {
|
||||||
|
printf("PAM module initialized\n");
|
||||||
|
retval = pam_authenticate(pamh, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can the accound be used at this time?
|
||||||
|
if (retval == PAM_SUCCESS) {
|
||||||
|
printf("Credentials accepted.\n");
|
||||||
|
retval = pam_acct_mgmt(pamh, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Did everything work?
|
||||||
|
if (retval == PAM_SUCCESS) {
|
||||||
|
printf("Account is valid.\n");
|
||||||
|
printf("Authenticated\n");
|
||||||
|
} else {
|
||||||
|
printf("Not Authenticated\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
// close PAM (end session)
|
||||||
|
if (pam_end(pamh, retval) != PAM_SUCCESS) {
|
||||||
|
pamh = NULL;
|
||||||
|
printf("check_user: failed to release authenticator\n");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return retval == PAM_SUCCESS ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
16
pam/Cargo.toml
Normal file
16
pam/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
|
||||||
|
name = "pam-bindings"
|
||||||
|
description = "PAM bindings for Rust"
|
||||||
|
version = "0.1.1"
|
||||||
|
authors = [ "Anthony Nowell <anowell@gmail.com>" ]
|
||||||
|
repository = "https://github.com/anowell/pam-rs"
|
||||||
|
readme = "../README.md"
|
||||||
|
keywords = ["pam", "ffi", "linux", "authentication"]
|
||||||
|
license = "MIT"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "pam"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
libc = "0.2.97"
|
||||||
66
pam/src/constants.rs
Normal file
66
pam/src/constants.rs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
use libc::{c_int, c_uint};
|
||||||
|
|
||||||
|
// TODO: Import constants from C header file at compile time.
|
||||||
|
|
||||||
|
pub type PamFlag = c_uint;
|
||||||
|
pub type PamItemType = c_int;
|
||||||
|
pub type PamMessageStyle = c_int;
|
||||||
|
|
||||||
|
// The Linux-PAM flags
|
||||||
|
// see /usr/include/security/_pam_types.h
|
||||||
|
pub const PAM_SILENT: PamFlag = 0x8000;
|
||||||
|
pub const PAM_DISALLOW_NULL_AUTHTOK: PamFlag = 0x0001;
|
||||||
|
pub const PAM_ESTABLISH_CRED: PamFlag = 0x0002;
|
||||||
|
pub const PAM_DELETE_CRED: PamFlag = 0x0004;
|
||||||
|
pub const PAM_REINITIALIZE_CRED: PamFlag = 0x0008;
|
||||||
|
pub const PAM_REFRESH_CRED: PamFlag = 0x0010;
|
||||||
|
pub const PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020;
|
||||||
|
|
||||||
|
// Message styles
|
||||||
|
pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1;
|
||||||
|
pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2;
|
||||||
|
pub const PAM_ERROR_MSG: PamMessageStyle = 3;
|
||||||
|
pub const PAM_TEXT_INFO: PamMessageStyle = 4;
|
||||||
|
/// yes/no/maybe conditionals
|
||||||
|
pub const PAM_RADIO_TYPE: PamMessageStyle = 5;
|
||||||
|
pub const PAM_BINARY_PROMPT: PamMessageStyle = 7;
|
||||||
|
|
||||||
|
// The Linux-PAM return values
|
||||||
|
// see /usr/include/security/_pam_types.h
|
||||||
|
#[allow(non_camel_case_types, dead_code)]
|
||||||
|
#[derive(Debug, PartialEq)]
|
||||||
|
#[repr(C)]
|
||||||
|
pub enum PamResultCode {
|
||||||
|
PAM_SUCCESS = 0,
|
||||||
|
PAM_OPEN_ERR = 1,
|
||||||
|
PAM_SYMBOL_ERR = 2,
|
||||||
|
PAM_SERVICE_ERR = 3,
|
||||||
|
PAM_SYSTEM_ERR = 4,
|
||||||
|
PAM_BUF_ERR = 5,
|
||||||
|
PAM_PERM_DENIED = 6,
|
||||||
|
PAM_AUTH_ERR = 7,
|
||||||
|
PAM_CRED_INSUFFICIENT = 8,
|
||||||
|
PAM_AUTHINFO_UNAVAIL = 9,
|
||||||
|
PAM_USER_UNKNOWN = 10,
|
||||||
|
PAM_MAXTRIES = 11,
|
||||||
|
PAM_NEW_AUTHTOK_REQD = 12,
|
||||||
|
PAM_ACCT_EXPIRED = 13,
|
||||||
|
PAM_SESSION_ERR = 14,
|
||||||
|
PAM_CRED_UNAVAIL = 15,
|
||||||
|
PAM_CRED_EXPIRED = 16,
|
||||||
|
PAM_CRED_ERR = 17,
|
||||||
|
PAM_NO_MODULE_DATA = 18,
|
||||||
|
PAM_CONV_ERR = 19,
|
||||||
|
PAM_AUTHTOK_ERR = 20,
|
||||||
|
PAM_AUTHTOK_RECOVERY_ERR = 21,
|
||||||
|
PAM_AUTHTOK_LOCK_BUSY = 22,
|
||||||
|
PAM_AUTHTOK_DISABLE_AGING = 23,
|
||||||
|
PAM_TRY_AGAIN = 24,
|
||||||
|
PAM_IGNORE = 25,
|
||||||
|
PAM_ABORT = 26,
|
||||||
|
PAM_AUTHTOK_EXPIRED = 27,
|
||||||
|
PAM_MODULE_UNKNOWN = 28,
|
||||||
|
PAM_BAD_ITEM = 29,
|
||||||
|
PAM_CONV_AGAIN = 30,
|
||||||
|
PAM_INCOMPLETE = 31,
|
||||||
|
}
|
||||||
94
pam/src/conv.rs
Normal file
94
pam/src/conv.rs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
use libc::{c_char, c_int};
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use constants::PamResultCode;
|
||||||
|
use constants::PamMessageStyle;
|
||||||
|
use items::Item;
|
||||||
|
use module::PamResult;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct PamMessage {
|
||||||
|
msg_style: PamMessageStyle,
|
||||||
|
msg: *const c_char,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct PamResponse {
|
||||||
|
resp: *const c_char,
|
||||||
|
resp_retcode: libc::c_int, // Unused - always zero
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `PamConv` acts as a channel for communicating with user.
|
||||||
|
///
|
||||||
|
/// Communication is mediated by the pam client (the application that invoked
|
||||||
|
/// pam). Messages sent will be relayed to the user by the client, and response
|
||||||
|
/// will be relayed back.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct Inner {
|
||||||
|
conv: extern "C" fn(
|
||||||
|
num_msg: c_int,
|
||||||
|
pam_message: &&PamMessage,
|
||||||
|
pam_response: &mut *const PamResponse,
|
||||||
|
appdata_ptr: *const libc::c_void,
|
||||||
|
) -> PamResultCode,
|
||||||
|
appdata_ptr: *const libc::c_void,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Conv<'a>(&'a Inner);
|
||||||
|
|
||||||
|
impl<'a> Conv<'a> {
|
||||||
|
/// Sends a message to the pam client.
|
||||||
|
///
|
||||||
|
/// This will typically result in the user seeing a message or a prompt.
|
||||||
|
/// There are several message styles available:
|
||||||
|
///
|
||||||
|
/// - PAM_PROMPT_ECHO_OFF
|
||||||
|
/// - PAM_PROMPT_ECHO_ON
|
||||||
|
/// - PAM_ERROR_MSG
|
||||||
|
/// - PAM_TEXT_INFO
|
||||||
|
/// - PAM_RADIO_TYPE
|
||||||
|
/// - PAM_BINARY_PROMPT
|
||||||
|
///
|
||||||
|
/// Note that the user experience will depend on how the client implements
|
||||||
|
/// these message styles - and not all applications implement all message
|
||||||
|
/// styles.
|
||||||
|
pub fn send(&self, style: PamMessageStyle, msg: &str) -> PamResult<Option<&CStr>> {
|
||||||
|
let mut resp_ptr: *const PamResponse = ptr::null();
|
||||||
|
let msg_cstr = CString::new(msg).unwrap();
|
||||||
|
let msg = PamMessage {
|
||||||
|
msg_style: style,
|
||||||
|
msg: msg_cstr.as_ptr(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let ret = (self.0.conv)(1, &&msg, &mut resp_ptr, self.0.appdata_ptr);
|
||||||
|
|
||||||
|
if PamResultCode::PAM_SUCCESS == ret {
|
||||||
|
// PamResponse.resp is null for styles that don't return user input like PAM_TEXT_INFO
|
||||||
|
let response = unsafe { (*resp_ptr).resp };
|
||||||
|
if response.is_null() {
|
||||||
|
Ok(None)
|
||||||
|
} else {
|
||||||
|
Ok(Some(unsafe { CStr::from_ptr(response) }))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Err(ret)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> Item for Conv<'a> {
|
||||||
|
type Raw = Inner;
|
||||||
|
|
||||||
|
fn type_id() -> crate::items::ItemType {
|
||||||
|
crate::items::ItemType::Conv
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_raw(raw: *const Self::Raw) -> Self {
|
||||||
|
Self(&*raw)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw(self) -> *const Self::Raw {
|
||||||
|
self.0 as _
|
||||||
|
}
|
||||||
|
}
|
||||||
88
pam/src/items.rs
Normal file
88
pam/src/items.rs
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
#[repr(u32)]
|
||||||
|
pub enum ItemType {
|
||||||
|
/// The service name
|
||||||
|
Service = 1,
|
||||||
|
/// The user name
|
||||||
|
User = 2,
|
||||||
|
/// The tty name
|
||||||
|
Tty = 3,
|
||||||
|
/// The remote host name
|
||||||
|
RHost = 4,
|
||||||
|
/// The pam_conv structure
|
||||||
|
Conv = 5,
|
||||||
|
/// The authentication token (password)
|
||||||
|
AuthTok = 6,
|
||||||
|
/// The old authentication token
|
||||||
|
OldAuthTok = 7,
|
||||||
|
/// The remote user name
|
||||||
|
RUser = 8,
|
||||||
|
/// the prompt for getting a username
|
||||||
|
UserPrompt = 9,
|
||||||
|
/// app supplied function to override failure delays
|
||||||
|
FailDelay = 10,
|
||||||
|
/// X :display name
|
||||||
|
XDisplay = 11,
|
||||||
|
/// X :server authentication data
|
||||||
|
XAuthData = 12,
|
||||||
|
/// The type for pam_get_authtok
|
||||||
|
AuthTokType = 13,
|
||||||
|
}
|
||||||
|
|
||||||
|
// A type that can be requested by `pam::Handle::get_item`.
|
||||||
|
pub trait Item {
|
||||||
|
/// The `repr(C)` type that is returned (by pointer) by the underlying `pam_get_item` function.
|
||||||
|
type Raw;
|
||||||
|
|
||||||
|
/// The `ItemType` for this type
|
||||||
|
fn type_id() -> ItemType;
|
||||||
|
|
||||||
|
/// The function to convert from the pointer to the C-representation to this safer wrapper type
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function can assume the pointer is a valid pointer to a `Self::Raw` instance.
|
||||||
|
unsafe fn from_raw(raw: *const Self::Raw) -> Self;
|
||||||
|
|
||||||
|
/// The function to convert from this wrapper type to a C-compatible pointer.
|
||||||
|
fn into_raw(self) -> *const Self::Raw;
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! cstr_item {
|
||||||
|
($name:ident) => {
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct $name<'s>(pub &'s std::ffi::CStr);
|
||||||
|
|
||||||
|
impl<'s> std::ops::Deref for $name<'s> {
|
||||||
|
type Target = &'s std::ffi::CStr;
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> Item for $name<'s> {
|
||||||
|
type Raw = libc::c_char;
|
||||||
|
|
||||||
|
fn type_id() -> ItemType {
|
||||||
|
ItemType::$name
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe fn from_raw(raw: *const Self::Raw) -> Self {
|
||||||
|
Self(std::ffi::CStr::from_ptr(raw))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_raw(self) -> *const Self::Raw {
|
||||||
|
self.0.as_ptr()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
cstr_item!(Service);
|
||||||
|
cstr_item!(User);
|
||||||
|
cstr_item!(Tty);
|
||||||
|
cstr_item!(RHost);
|
||||||
|
// Conv
|
||||||
|
cstr_item!(AuthTok);
|
||||||
|
cstr_item!(OldAuthTok);
|
||||||
|
cstr_item!(RUser);
|
||||||
|
cstr_item!(UserPrompt);
|
||||||
34
pam/src/lib.rs
Executable file
34
pam/src/lib.rs
Executable file
@@ -0,0 +1,34 @@
|
|||||||
|
//! Interface to the pluggable authentication module framework (PAM).
|
||||||
|
//!
|
||||||
|
//! The goal of this library is to provide a type-safe API that can be used to
|
||||||
|
//! interact with PAM. The library is incomplete - currently it supports
|
||||||
|
//! a subset of functions for use in a pam authentication module. A pam module
|
||||||
|
//! is a shared library that is invoked to authenticate a user, or to perform
|
||||||
|
//! other functions.
|
||||||
|
//!
|
||||||
|
//! For general information on writing pam modules, see
|
||||||
|
//! [The Linux-PAM Module Writers' Guide][module-guide]
|
||||||
|
//!
|
||||||
|
//! [module-guide]: http://www.linux-pam.org/Linux-PAM-html/Linux-PAM_MWG.html
|
||||||
|
//!
|
||||||
|
//! A typical authentication module will define an external function called
|
||||||
|
//! `pam_sm_authenticate()`, which will use functions in this library to
|
||||||
|
//! interrogate the program that requested authentication for more information,
|
||||||
|
//! and to render a result. For a working example that uses this library, see
|
||||||
|
//! [toznyauth-pam][].
|
||||||
|
//!
|
||||||
|
//! [toznyauth-pam]: https://github.com/tozny/toznyauth-pam
|
||||||
|
//!
|
||||||
|
//! Note that constants that are normally read from pam header files are
|
||||||
|
//! hard-coded in the `constants` module. The values there are taken from
|
||||||
|
//! a Linux system. That means that it might take some work to get this library
|
||||||
|
//! to work on other platforms.
|
||||||
|
|
||||||
|
extern crate libc;
|
||||||
|
|
||||||
|
pub mod constants;
|
||||||
|
pub mod conv;
|
||||||
|
pub mod items;
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod macros;
|
||||||
|
pub mod module;
|
||||||
141
pam/src/macros.rs
Normal file
141
pam/src/macros.rs
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/// Macro to generate the `extern "C"` entrypoint bindings needed by PAM
|
||||||
|
///
|
||||||
|
/// You can call `pam_hooks!(SomeType);` for any type that implements `PamHooks`
|
||||||
|
///
|
||||||
|
/// ## Examples:
|
||||||
|
///
|
||||||
|
/// Here is full example of a PAM module that would authenticate and authorize everybody:
|
||||||
|
///
|
||||||
|
/// ```
|
||||||
|
/// #[macro_use] extern crate pam;
|
||||||
|
///
|
||||||
|
/// use pam::module::{PamHooks, PamHandle};
|
||||||
|
/// use pam::constants::{PamResultCode, PamFlag};
|
||||||
|
/// use std::ffi::CStr;
|
||||||
|
///
|
||||||
|
/// # fn main() {}
|
||||||
|
/// struct MyPamModule;
|
||||||
|
/// pam_hooks!(MyPamModule);
|
||||||
|
///
|
||||||
|
/// impl PamHooks for MyPamModule {
|
||||||
|
/// fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
/// println!("Everybody is authenticated!");
|
||||||
|
/// PamResultCode::PAM_SUCCESS
|
||||||
|
/// }
|
||||||
|
///
|
||||||
|
/// fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
/// println!("Everybody is authorized!");
|
||||||
|
/// PamResultCode::PAM_SUCCESS
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! pam_hooks {
|
||||||
|
($ident:ident) => {
|
||||||
|
pub use self::pam_hooks_scope::*;
|
||||||
|
mod pam_hooks_scope {
|
||||||
|
use std::ffi::CStr;
|
||||||
|
use std::os::raw::{c_char, c_int};
|
||||||
|
use $crate::constants::{PamFlag, PamResultCode};
|
||||||
|
use $crate::module::{PamHandle, PamHooks};
|
||||||
|
|
||||||
|
fn extract_argv<'a>(argc: c_int, argv: *const *const c_char) -> Vec<&'a CStr> {
|
||||||
|
(0..argc)
|
||||||
|
.map(|o| unsafe { CStr::from_ptr(*argv.offset(o as isize) as *const c_char) })
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn pam_sm_acct_mgmt(
|
||||||
|
pamh: &mut PamHandle,
|
||||||
|
flags: PamFlag,
|
||||||
|
argc: c_int,
|
||||||
|
argv: *const *const c_char,
|
||||||
|
) -> PamResultCode {
|
||||||
|
let args = extract_argv(argc, argv);
|
||||||
|
super::$ident::acct_mgmt(pamh, args, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn pam_sm_authenticate(
|
||||||
|
pamh: &mut PamHandle,
|
||||||
|
flags: PamFlag,
|
||||||
|
argc: c_int,
|
||||||
|
argv: *const *const c_char,
|
||||||
|
) -> PamResultCode {
|
||||||
|
let args = extract_argv(argc, argv);
|
||||||
|
super::$ident::sm_authenticate(pamh, args, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn pam_sm_chauthtok(
|
||||||
|
pamh: &mut PamHandle,
|
||||||
|
flags: PamFlag,
|
||||||
|
argc: c_int,
|
||||||
|
argv: *const *const c_char,
|
||||||
|
) -> PamResultCode {
|
||||||
|
let args = extract_argv(argc, argv);
|
||||||
|
super::$ident::sm_chauthtok(pamh, args, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn pam_sm_close_session(
|
||||||
|
pamh: &mut PamHandle,
|
||||||
|
flags: PamFlag,
|
||||||
|
argc: c_int,
|
||||||
|
argv: *const *const c_char,
|
||||||
|
) -> PamResultCode {
|
||||||
|
let args = extract_argv(argc, argv);
|
||||||
|
super::$ident::sm_close_session(pamh, args, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn pam_sm_open_session(
|
||||||
|
pamh: &mut PamHandle,
|
||||||
|
flags: PamFlag,
|
||||||
|
argc: c_int,
|
||||||
|
argv: *const *const c_char,
|
||||||
|
) -> PamResultCode {
|
||||||
|
let args = extract_argv(argc, argv);
|
||||||
|
super::$ident::sm_open_session(pamh, args, flags)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn pam_sm_setcred(
|
||||||
|
pamh: &mut PamHandle,
|
||||||
|
flags: PamFlag,
|
||||||
|
argc: c_int,
|
||||||
|
argv: *const *const c_char,
|
||||||
|
) -> PamResultCode {
|
||||||
|
let args = extract_argv(argc, argv);
|
||||||
|
super::$ident::sm_setcred(pamh, args, flags)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[macro_export]
|
||||||
|
macro_rules! pam_try {
|
||||||
|
($r:expr) => {
|
||||||
|
match $r {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(e) => return e,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
($r:expr, $e:expr) => {
|
||||||
|
match $r {
|
||||||
|
Ok(t) => t,
|
||||||
|
Err(_) => return $e,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod test {
|
||||||
|
use module::PamHooks;
|
||||||
|
|
||||||
|
struct Foo;
|
||||||
|
impl PamHooks for Foo {}
|
||||||
|
|
||||||
|
pam_hooks!(Foo);
|
||||||
|
}
|
||||||
255
pam/src/module.rs
Executable file
255
pam/src/module.rs
Executable file
@@ -0,0 +1,255 @@
|
|||||||
|
//! Functions for use in pam modules.
|
||||||
|
|
||||||
|
use libc::c_char;
|
||||||
|
use std::ffi::{CStr, CString};
|
||||||
|
|
||||||
|
use constants::{PamFlag, PamResultCode};
|
||||||
|
|
||||||
|
/// Opaque type, used as a pointer when making pam API calls.
|
||||||
|
///
|
||||||
|
/// A module is invoked via an external function such as `pam_sm_authenticate`.
|
||||||
|
/// Such a call provides a pam handle pointer. The same pointer should be given
|
||||||
|
/// as an argument when making API calls.
|
||||||
|
#[repr(C)]
|
||||||
|
pub struct PamHandle {
|
||||||
|
_data: [u8; 0],
|
||||||
|
}
|
||||||
|
|
||||||
|
#[link(name = "pam")]
|
||||||
|
extern "C" {
|
||||||
|
fn pam_get_data(
|
||||||
|
pamh: *const PamHandle,
|
||||||
|
module_data_name: *const c_char,
|
||||||
|
data: &mut *const libc::c_void,
|
||||||
|
) -> PamResultCode;
|
||||||
|
|
||||||
|
fn pam_set_data(
|
||||||
|
pamh: *const PamHandle,
|
||||||
|
module_data_name: *const c_char,
|
||||||
|
data: *mut libc::c_void,
|
||||||
|
cleanup: extern "C" fn(
|
||||||
|
pamh: *const PamHandle,
|
||||||
|
data: *mut libc::c_void,
|
||||||
|
error_status: PamResultCode,
|
||||||
|
),
|
||||||
|
) -> PamResultCode;
|
||||||
|
|
||||||
|
fn pam_get_item(
|
||||||
|
pamh: *const PamHandle,
|
||||||
|
item_type: crate::items::ItemType,
|
||||||
|
item: &mut *const libc::c_void,
|
||||||
|
) -> PamResultCode;
|
||||||
|
|
||||||
|
fn pam_set_item(
|
||||||
|
pamh: *mut PamHandle,
|
||||||
|
item_type: crate::items::ItemType,
|
||||||
|
item: *const libc::c_void,
|
||||||
|
) -> PamResultCode;
|
||||||
|
|
||||||
|
fn pam_get_user(
|
||||||
|
pamh: *const PamHandle,
|
||||||
|
user: &*mut c_char,
|
||||||
|
prompt: *const c_char,
|
||||||
|
) -> PamResultCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub extern "C" fn cleanup<T>(_: *const PamHandle, c_data: *mut libc::c_void, _: PamResultCode) {
|
||||||
|
unsafe {
|
||||||
|
let _data: Box<T> = Box::from_raw(c_data.cast::<T>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type PamResult<T> = Result<T, PamResultCode>;
|
||||||
|
|
||||||
|
impl PamHandle {
|
||||||
|
/// Gets some value, identified by `key`, that has been set by the module
|
||||||
|
/// previously.
|
||||||
|
///
|
||||||
|
/// See `pam_get_data` in
|
||||||
|
/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the underlying PAM function call fails.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// The data stored under the provided key must be of type `T` otherwise the
|
||||||
|
/// behaviour of this funtion is undefined.
|
||||||
|
pub unsafe fn get_data<'a, T>(&'a self, key: &str) -> PamResult<&'a T> {
|
||||||
|
let c_key = CString::new(key).unwrap();
|
||||||
|
let mut ptr: *const libc::c_void = std::ptr::null();
|
||||||
|
let res = pam_get_data(self, c_key.as_ptr(), &mut ptr);
|
||||||
|
if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
|
||||||
|
let typed_ptr = ptr.cast::<T>();
|
||||||
|
let data: &T = &*typed_ptr;
|
||||||
|
Ok(data)
|
||||||
|
} else {
|
||||||
|
Err(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stores a value that can be retrieved later with `get_data`. The value lives
|
||||||
|
/// as long as the current pam cycle.
|
||||||
|
///
|
||||||
|
/// See `pam_set_data` in
|
||||||
|
/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the underlying PAM function call fails.
|
||||||
|
pub fn set_data<T>(&self, key: &str, data: Box<T>) -> PamResult<()> {
|
||||||
|
let c_key = CString::new(key).unwrap();
|
||||||
|
let res = unsafe {
|
||||||
|
pam_set_data(
|
||||||
|
self,
|
||||||
|
c_key.as_ptr(),
|
||||||
|
Box::into_raw(data).cast::<libc::c_void>(),
|
||||||
|
cleanup::<T>,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
if PamResultCode::PAM_SUCCESS == res {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves a value that has been set, possibly by the pam client. This is
|
||||||
|
/// particularly useful for getting a `PamConv` reference.
|
||||||
|
///
|
||||||
|
/// See `pam_get_item` in
|
||||||
|
/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the underlying PAM function call fails.
|
||||||
|
pub fn get_item<T: crate::items::Item>(&self) -> PamResult<Option<T>> {
|
||||||
|
let mut ptr: *const libc::c_void = std::ptr::null();
|
||||||
|
let (res, item) = unsafe {
|
||||||
|
let r = pam_get_item(self, T::type_id(), &mut ptr);
|
||||||
|
let typed_ptr = ptr.cast::<T::Raw>();
|
||||||
|
let t = if typed_ptr.is_null() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(T::from_raw(typed_ptr))
|
||||||
|
};
|
||||||
|
(r, t)
|
||||||
|
};
|
||||||
|
if PamResultCode::PAM_SUCCESS == res {
|
||||||
|
Ok(item)
|
||||||
|
} else {
|
||||||
|
Err(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets a value in the pam context. The value can be retrieved using
|
||||||
|
/// `get_item`.
|
||||||
|
///
|
||||||
|
/// Note that all items are strings, except `PAM_CONV` and `PAM_FAIL_DELAY`.
|
||||||
|
///
|
||||||
|
/// See `pam_set_item` in
|
||||||
|
/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the underlying PAM function call fails.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the provided item key contains a nul byte
|
||||||
|
pub fn set_item_str<T: crate::items::Item>(&mut self, item: T) -> PamResult<()> {
|
||||||
|
let res =
|
||||||
|
unsafe { pam_set_item(self, T::type_id(), item.into_raw().cast::<libc::c_void>())};
|
||||||
|
if PamResultCode::PAM_SUCCESS == res {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Retrieves the name of the user who is authenticating or logging in.
|
||||||
|
///
|
||||||
|
/// This is really a specialization of `get_item`.
|
||||||
|
///
|
||||||
|
/// See `pam_get_user` in
|
||||||
|
/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
|
||||||
|
///
|
||||||
|
/// # Errors
|
||||||
|
///
|
||||||
|
/// Returns an error if the underlying PAM function call fails.
|
||||||
|
///
|
||||||
|
/// # Panics
|
||||||
|
///
|
||||||
|
/// Panics if the provided prompt string contains a nul byte
|
||||||
|
pub fn get_user(&self, prompt: Option<&str>) -> PamResult<String> {
|
||||||
|
let ptr: *mut c_char = std::ptr::null_mut();
|
||||||
|
let prompt_string;
|
||||||
|
let c_prompt = match prompt {
|
||||||
|
Some(p) => {
|
||||||
|
prompt_string = CString::new(p).unwrap();
|
||||||
|
prompt_string.as_ptr()
|
||||||
|
}
|
||||||
|
None => std::ptr::null(),
|
||||||
|
};
|
||||||
|
let res = unsafe { pam_get_user(self, &ptr, c_prompt) };
|
||||||
|
if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
|
||||||
|
let const_ptr = ptr as *const c_char;
|
||||||
|
let bytes = unsafe { CStr::from_ptr(const_ptr).to_bytes() };
|
||||||
|
String::from_utf8(bytes.to_vec()).map_err(|_| PamResultCode::PAM_CONV_ERR)
|
||||||
|
} else {
|
||||||
|
Err(res)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Provides functions that are invoked by the entrypoints generated by the
|
||||||
|
/// [`pam_hooks!` macro](../macro.pam_hooks.html).
|
||||||
|
///
|
||||||
|
/// All of hooks are ignored by PAM dispatch by default given the default return value of `PAM_IGNORE`.
|
||||||
|
/// Override any functions that you want to handle with your module. See `man pam(3)`.
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
pub trait PamHooks {
|
||||||
|
/// This function performs the task of establishing whether the user is permitted to gain access at
|
||||||
|
/// this time. It should be understood that the user has previously been validated by an
|
||||||
|
/// authentication module. This function checks for other things. Such things might be: the time of
|
||||||
|
/// day or the date, the terminal line, remote hostname, etc. This function may also determine
|
||||||
|
/// things like the expiration on passwords, and respond that the user change it before continuing.
|
||||||
|
fn acct_mgmt(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
PamResultCode::PAM_IGNORE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function performs the task of authenticating the user.
|
||||||
|
fn sm_authenticate(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
PamResultCode::PAM_IGNORE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function is used to (re-)set the authentication token of the user.
|
||||||
|
///
|
||||||
|
/// The PAM library calls this function twice in succession. The first time with
|
||||||
|
/// `PAM_PRELIM_CHECK` and then, if the module does not return `PAM_TRY_AGAIN`, subsequently with
|
||||||
|
/// `PAM_UPDATE_AUTHTOK`. It is only on the second call that the authorization token is
|
||||||
|
/// (possibly) changed.
|
||||||
|
fn sm_chauthtok(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
PamResultCode::PAM_IGNORE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function is called to terminate a session.
|
||||||
|
fn sm_close_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
PamResultCode::PAM_IGNORE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function is called to commence a session.
|
||||||
|
fn sm_open_session(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
PamResultCode::PAM_IGNORE
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function performs the task of altering the credentials of the user with respect to the
|
||||||
|
/// corresponding authorization scheme. Generally, an authentication module may have access to more
|
||||||
|
/// information about a user than their authentication token. This function is used to make such
|
||||||
|
/// information available to the application. It should only be called after the user has been
|
||||||
|
/// authenticated but before a session has been established.
|
||||||
|
fn sm_setcred(pamh: &mut PamHandle, args: Vec<&CStr>, flags: PamFlag) -> PamResultCode {
|
||||||
|
PamResultCode::PAM_IGNORE
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user