changeset 15:27730595f1ea

Adding pam-http module
author Anthony Nowell <anthony@algorithmia.com>
date Sun, 24 Sep 2017 00:22:29 -0600
parents 51b097c12d3c
children f64ee7b6cdf1
files .gitignore Cargo.toml README.md pam-http/Cargo.toml pam-http/Justfile pam-http/conf/http-auth pam-http/src/ffi.rs pam-http/src/lib.rs pam-http/test.c pam/Cargo.toml pam/README.md pam/src/constants.rs pam/src/conv.rs pam/src/items.rs pam/src/lib.rs pam/src/module.rs src/constants.rs src/conv.rs src/items.rs src/lib.rs src/module.rs
diffstat 21 files changed, 743 insertions(+), 490 deletions(-) [+]
line wrap: on
line diff
--- a/.gitignore	Sat Sep 23 14:30:18 2017 -0600
+++ b/.gitignore	Sun Sep 24 00:22:29 2017 -0600
@@ -8,7 +8,7 @@
 *.exe
 
 # Generated by Cargo
-/target/
+target/
 Cargo.lock
 
 # Override top-level .gitignore in Misc repo
--- a/Cargo.toml	Sat Sep 23 14:30:18 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,18 +0,0 @@
-[package]
-
-name = "pam"
-description = "PAM bindings for Rust"
-version = "0.0.2"
-authors = [ "Jesse Hallett <jesse@galois.com>" ]
-documentation = "https://tozny.github.io/rust-pam/pam/"
-homepage = "https://github.com/tozny/rust-pam"
-repository = "https://github.com/tozny/rust-pam.git"
-readme = "README.md"
-keywords = ["pam", "ffi", "linux", "authentication"]
-license = "MIT"
-
-[lib]
-name = "pam"
-
-[dependencies]
-libc = "~0.1.5"
--- a/README.md	Sat Sep 23 14:30:18 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,13 +0,0 @@
-rust-pam
-========
-
-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.
-
-For more information, see the [package documentation][doc].
-
-[doc]: https://tozny.github.io/rust-pam/pam/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam-http/Cargo.toml	Sun Sep 24 00:22:29 2017 -0600
@@ -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 = { path = "../pam/" }
+reqwest = "0.7"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam-http/Justfile	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,12 @@
+
+all:
+    cargo build
+
+install:
+    @cargo build
+    sudo cp conf/http-auth /etc/pam.d/
+    sudo cp target/debug/libpam_http.so /lib/security/pam_http.so
+
+test:
+    @just install
+    gcc -o target/pam_test test.c -lpam -lpam_misc
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam-http/conf/http-auth	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,2 @@
+auth sufficient pam_http.so url=http://localhost:3000
+account sufficient pam_http.so
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam-http/src/ffi.rs	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,82 @@
+use pam::module::{PamHandleT};
+use pam::constants::{PamFlag, PamResultCode, PAM_SILENT};
+use std::ffi::CStr;
+use std::os::raw::{c_char, c_int};
+
+
+
+fn extract_argv(argc: c_int, argv: *const *const c_char) -> Vec<String> {
+    (0..argc)
+        .map(|o| unsafe {
+            CStr::from_ptr(*argv.offset(o as isize) as *const c_char)
+                .to_string_lossy()
+                .into_owned()
+        })
+        .collect()
+}
+
+#[no_mangle]
+pub extern "C" fn pam_sm_acct_mgmt(
+	pamh: &PamHandleT,
+	flags: PamFlag,
+	argc: c_int,
+	argv: *const *const c_char,
+) -> PamResultCode {
+	let args = extract_argv(argc, argv);
+	let silent = (flags & PAM_SILENT) != 0;
+	super::acct_mgmt(pamh, args, silent)
+}
+
+#[no_mangle]
+pub extern "C" fn pam_sm_authenticate(
+	pamh: &PamHandleT,
+	flags: PamFlag,
+	argc: c_int,
+	argv: *const *const c_char,
+) -> PamResultCode {
+	let args = extract_argv(argc, argv);
+	let silent = (flags & PAM_SILENT) != 0;
+	super::sm_authenticate(pamh, args, silent)
+}
+
+#[no_mangle]
+pub extern "C" fn pam_sm_chauthtok(
+	_: &PamHandleT,
+	_: PamFlag,
+	_: c_int,
+	_: *const *const c_char,
+) -> PamResultCode {
+	PamResultCode::PAM_IGNORE
+}
+
+#[no_mangle]
+pub extern "C" fn pam_sm_close_session(
+	_: &PamHandleT,
+	_: PamFlag,
+	_: c_int,
+	_: *const *const c_char,
+) -> PamResultCode {
+	PamResultCode::PAM_IGNORE
+}
+
+#[no_mangle]
+pub extern "C" fn pam_sm_open_session(
+	_: &PamHandleT,
+	_: PamFlag,
+	_: c_int,
+	_: *const *const c_char,
+) -> PamResultCode {
+	PamResultCode::PAM_IGNORE
+}
+
+#[no_mangle]
+pub extern "C" fn pam_sm_setcred(
+	pamh: &PamHandleT,
+	flags: PamFlag,
+	argc: c_int,
+	argv: *const *const c_char,
+) -> PamResultCode {
+	let args = extract_argv(argc, argv);
+	let silent = (flags & PAM_SILENT) != 0;
+	super::sm_setcred(pamh, args, silent)
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam-http/src/lib.rs	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,93 @@
+extern crate pam;
+extern crate reqwest;
+
+pub mod ffi;
+
+use pam::module::{PamHandleT, get_item, get_user};
+use pam::constants::{PamResultCode, PAM_PROMPT_ECHO_OFF};
+use pam::conv::PamConv;
+use std::collections::HashMap;
+use std::time::Duration;
+use reqwest::{Client, StatusCode};
+
+macro_rules! pam_try {
+    ($e:expr) => (
+        match $e {
+            Ok(v) => v,
+            Err(e) => return e,
+        }
+    );
+    ($e:expr, $err:expr) => (
+        match $e {
+            Ok(v) => v,
+            Err(e) => {
+                println!("Error: {}", e);
+                return $err;
+            }
+        }
+    );
+}
+
+// This function performs the task of authenticating the user.
+pub fn sm_authenticate(pamh: &PamHandleT, args: Vec<String>, silent: bool) -> PamResultCode {
+    println!("Let's auth over HTTP");
+
+    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!(get_user(&pamh, None));
+
+    let url: &str = match args.get("url") {
+        Some(url) => url,
+        None => return PamResultCode::PAM_AUTH_ERR,
+    };
+    let ca_file = args.get("ca_file");
+
+    let conv = match get_item::<PamConv>(&pamh) {
+        Ok(conv) => conv,
+        Err(err) => {
+            println!("Couldn't get pam_conv");
+            return err;
+        }
+    };
+    let password = pam_try!(conv.send(PAM_PROMPT_ECHO_OFF, "Word, yo: "));
+    println!("Got a password {:?}", password);
+    let status = pam_try!(get_url(url, &user, password.as_ref().map(|p|&**p)), PamResultCode::PAM_AUTH_ERR);
+
+    if !status.is_success() {
+        println!("HTTP Error: {}", status);
+        return PamResultCode::PAM_AUTH_ERR;
+    }
+
+    PamResultCode::PAM_SUCCESS
+}
+
+fn get_url(url: &str, user: &str, password: Option<&str>) -> reqwest::Result<StatusCode> {
+    let client = Client::builder()?.timeout(Duration::from_secs(5)).build()?;
+    client.get(url)?
+        .basic_auth(user, password)
+        .send()
+        .map(|r| r.status())
+}
+
+// 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.
+pub fn sm_setcred(_pamh: &PamHandleT, _args: Vec<String>, _silent: bool) -> PamResultCode {
+    println!("set credentials");
+    PamResultCode::PAM_SUCCESS
+}
+
+// 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.
+pub fn acct_mgmt(_pamh: &PamHandleT, _args: Vec<String>, _silent: bool) -> PamResultCode {
+    println!("account management");
+    PamResultCode::PAM_SUCCESS
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam-http/test.c	Sun Sep 24 00:22:29 2017 -0600
@@ -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;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/Cargo.toml	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,18 @@
+[package]
+
+name = "pam"
+description = "PAM bindings for Rust"
+version = "0.0.2"
+authors = [ "Jesse Hallett <jesse@galois.com>" ]
+documentation = "https://tozny.github.io/rust-pam/pam/"
+homepage = "https://github.com/tozny/rust-pam"
+repository = "https://github.com/tozny/rust-pam.git"
+readme = "README.md"
+keywords = ["pam", "ffi", "linux", "authentication"]
+license = "MIT"
+
+[lib]
+name = "pam"
+
+[dependencies]
+libc = "~0.1.5"
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/README.md	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,13 @@
+rust-pam
+========
+
+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.
+
+For more information, see the [package documentation][doc].
+
+[doc]: https://tozny.github.io/rust-pam/pam/
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/src/constants.rs	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,97 @@
+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;
+pub type AlwaysZero = 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;
+
+// The Linux-PAM item types
+// see /usr/include/security/_pam_types.h
+/// The service name
+pub const PAM_SERVICE: PamItemType = 1;
+/// The user name
+pub const PAM_USER: PamItemType = 2;
+/// The tty name
+pub const PAM_TTY: PamItemType = 3;
+/// The remote host name
+pub const PAM_RHOST: PamItemType = 4;
+/// The pam_conv structure
+pub const PAM_CONV: PamItemType = 5;
+/// The authentication token (password)
+pub const PAM_AUTHTOK: PamItemType = 6;
+/// The old authentication token
+pub const PAM_OLDAUTHTOK: PamItemType = 7;
+/// The remote user name
+pub const PAM_RUSER: PamItemType = 8;
+/// the prompt for getting a username
+pub const PAM_USER_PROMPT: PamItemType = 9;
+/* Linux-PAM :extensionsPamItemType = */
+/// app supplied function to override failure delays
+pub const PAM_FAIL_DELAY: PamItemType = 10;
+/// X :display name
+pub const PAM_XDISPLAY: PamItemType = 11;
+/// X :server authentication data
+pub const PAM_XAUTHDATA: PamItemType = 12;
+/// The type for pam_get_authtok
+pub const PAM_AUTHTOK_TYPE: PamItemType = 13;
+
+// 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,
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/src/conv.rs	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,81 @@
+use libc::{c_char, c_int};
+use std::ptr;
+use std::ffi::{CStr, CString};
+
+use constants::PamResultCode;
+use constants::*;
+use module::{PamItem, PamResult};
+
+#[allow(missing_copy_implementations)]
+pub enum AppDataPtr {}
+
+#[repr(C)]
+struct PamMessage {
+    msg_style: PamMessageStyle,
+    msg: *const c_char,
+}
+
+#[repr(C)]
+struct PamResponse {
+    resp: *const c_char,
+    resp_retcode: AlwaysZero,
+}
+
+/// `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 PamConv {
+    conv: extern "C" fn(num_msg: c_int,
+                        pam_message: &&PamMessage,
+                        pam_response: &mut *const PamResponse,
+                        appdata_ptr: *const AppDataPtr)
+                        -> PamResultCode,
+    appdata_ptr: *const AppDataPtr,
+}
+
+impl PamConv {
+    /// 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<String>> {
+        let mut resp_ptr: *const PamResponse = ptr::null();
+        let msg = PamMessage {
+            msg_style: style,
+            msg: CString::new(msg).unwrap().as_ptr(),
+        };
+
+        let ret = (self.conv)(1, &&msg, &mut resp_ptr, self.appdata_ptr);
+
+        if PamResultCode::PAM_SUCCESS == ret {
+            if resp_ptr.is_null() {
+                Ok(None)
+            } else {
+                let bytes = unsafe { CStr::from_ptr((*resp_ptr).resp).to_bytes() };
+                Ok(String::from_utf8(bytes.to_vec()).ok())
+            }
+        } else {
+            Err(ret)
+        }
+    }
+}
+
+impl PamItem for PamConv {
+    fn item_type() -> PamItemType {
+        PAM_CONV
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/src/items.rs	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,69 @@
+use constants::{PamItemType, PAM_SERVICE, PAM_USER, PAM_USER_PROMPT, PAM_TTY, PAM_RUSER, PAM_RHOST,
+                PAM_AUTHTOK, PAM_OLDAUTHTOK};
+use module::PamItem;
+pub use conv::PamConv;
+
+
+pub struct PamService {}
+
+impl PamItem for PamService {
+    fn item_type() -> PamItemType {
+        PAM_SERVICE
+    }
+}
+
+pub struct PamUser {}
+
+impl PamItem for PamUser {
+    fn item_type() -> PamItemType {
+        PAM_USER
+    }
+}
+
+pub struct PamUserPrompt {}
+
+impl PamItem for PamUserPrompt {
+    fn item_type() -> PamItemType {
+        PAM_USER_PROMPT
+    }
+}
+
+pub struct PamTty {}
+
+impl PamItem for PamTty {
+    fn item_type() -> PamItemType {
+        PAM_TTY
+    }
+}
+
+pub struct PamRUser {}
+
+impl PamItem for PamRUser {
+    fn item_type() -> PamItemType {
+        PAM_RUSER
+    }
+}
+
+pub struct PamRHost {}
+
+impl PamItem for PamRHost {
+    fn item_type() -> PamItemType {
+        PAM_RHOST
+    }
+}
+
+pub struct PamAuthTok {}
+
+impl PamItem for PamAuthTok {
+    fn item_type() -> PamItemType {
+        PAM_AUTHTOK
+    }
+}
+
+pub struct PamOldAuthTok {}
+
+impl PamItem for PamOldAuthTok {
+    fn item_type() -> PamItemType {
+        PAM_OLDAUTHTOK
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/src/lib.rs	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,32 @@
+//! 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 conv;
+pub mod constants;
+pub mod items;
+pub mod module;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/pam/src/module.rs	Sun Sep 24 00:22:29 2017 -0600
@@ -0,0 +1,179 @@
+//! Functions for use in pam modules.
+
+use libc::c_char;
+use std::{mem, ptr};
+use std::ffi::{CStr, CString};
+
+use constants::{PamResultCode, PamItemType};
+
+/// 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.
+#[allow(missing_copy_implementations)]
+pub enum PamHandleT {}
+
+#[allow(missing_copy_implementations)]
+enum PamItemT {}
+
+#[allow(missing_copy_implementations)]
+pub enum PamDataT {}
+
+#[link(name = "pam")]
+extern "C" {
+    fn pam_get_data(pamh: *const PamHandleT,
+                    module_data_name: *const c_char,
+                    data: &mut *const PamDataT)
+                    -> PamResultCode;
+
+    fn pam_set_data(pamh: *const PamHandleT,
+                    module_data_name: *const c_char,
+                    data: Box<PamDataT>,
+                    cleanup: extern "C" fn(pamh: *const PamHandleT,
+                                           data: Box<PamDataT>,
+                                           error_status: PamResultCode))
+                    -> PamResultCode;
+
+    fn pam_get_item(pamh: *const PamHandleT,
+                    item_type: PamItemType,
+                    item: &mut *const PamItemT)
+                    -> PamResultCode;
+
+    fn pam_set_item(pamh: *mut PamHandleT,
+                    item_type: PamItemType,
+                    item: &PamItemT)
+                    -> PamResultCode;
+
+    fn pam_get_user(pamh: *const PamHandleT,
+                    user: &*mut c_char,
+                    prompt: *const c_char)
+                    -> PamResultCode;
+}
+
+pub type PamResult<T> = Result<T, PamResultCode>;
+
+/// Type-level mapping for safely retrieving values with `get_item`.
+///
+/// See `pam_get_item` in
+/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
+pub trait PamItem {
+    /// Maps a Rust type to a pam constant.
+    ///
+    /// For example, the type PamConv maps to the constant PAM_CONV.  The pam
+    /// API contract specifies that when the API function `pam_get_item` is
+    /// called with the constant PAM_CONV, it will return a value of type
+    /// `PamConv`.
+    fn item_type() -> PamItemType;
+}
+
+/// 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
+pub unsafe fn get_data<'a, T>(pamh: &'a PamHandleT, key: &str) -> PamResult<&'a T> {
+    let c_key = CString::new(key).unwrap().as_ptr();
+    let mut ptr: *const PamDataT = ptr::null();
+    let res = pam_get_data(pamh, c_key, &mut ptr);
+    if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
+        let typed_ptr: *const T = mem::transmute(ptr);
+        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
+pub fn set_data<T>(pamh: &PamHandleT, key: &str, data: Box<T>) -> PamResult<()> {
+    let c_key = CString::new(key).unwrap().as_ptr();
+    let res = unsafe {
+        let c_data: Box<PamDataT> = mem::transmute(data);
+        pam_set_data(pamh, c_key, c_data, cleanup::<T>)
+    };
+    if PamResultCode::PAM_SUCCESS == res {
+        Ok(())
+    } else {
+        Err(res)
+    }
+}
+
+#[no_mangle]
+pub extern "C" fn cleanup<T>(_: *const PamHandleT, c_data: Box<PamDataT>, _: PamResultCode) {
+    unsafe {
+        let data: Box<T> = mem::transmute(c_data);
+        mem::drop(data);
+    }
+}
+
+/// 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
+pub fn get_item<'a, T: PamItem>(pamh: &'a PamHandleT) -> PamResult<&'a T> {
+    let mut ptr: *const PamItemT = ptr::null();
+    let (res, item) = unsafe {
+        let r = pam_get_item(pamh, T::item_type(), &mut ptr);
+        let typed_ptr: *const T = mem::transmute(ptr);
+        let t: &T = &*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
+pub fn set_item_str<'a, T: PamItem>(pamh: &'a mut PamHandleT, item: &str) -> PamResult<()> {
+    let c_item = CString::new(item).unwrap().as_ptr();
+
+    let res = unsafe {
+        pam_set_item(pamh,
+                     T::item_type(),
+
+                     // unwrapping is okay here, as c_item will not be a NULL
+                     // pointer
+                     (c_item as *const PamItemT).as_ref().unwrap())
+    };
+    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
+pub fn get_user<'a>(pamh: &'a PamHandleT, prompt: Option<&str>) -> PamResult<String> {
+    let ptr: *mut c_char = ptr::null_mut();
+    let c_prompt = match prompt {
+        Some(p) => CString::new(p).unwrap().as_ptr(),
+        None => ptr::null(),
+    };
+    let res = unsafe { pam_get_user(pamh, &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)
+    }
+}
--- a/src/constants.rs	Sat Sep 23 14:30:18 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,97 +0,0 @@
-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;
-pub type AlwaysZero = 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;
-
-// The Linux-PAM item types
-// see /usr/include/security/_pam_types.h
-/// The service name
-pub const PAM_SERVICE: PamItemType = 1;
-/// The user name
-pub const PAM_USER: PamItemType = 2;
-/// The tty name
-pub const PAM_TTY: PamItemType = 3;
-/// The remote host name
-pub const PAM_RHOST: PamItemType = 4;
-/// The pam_conv structure
-pub const PAM_CONV: PamItemType = 5;
-/// The authentication token (password)
-pub const PAM_AUTHTOK: PamItemType = 6;
-/// The old authentication token
-pub const PAM_OLDAUTHTOK: PamItemType = 7;
-/// The remote user name
-pub const PAM_RUSER: PamItemType = 8;
-/// the prompt for getting a username
-pub const PAM_USER_PROMPT: PamItemType = 9;
-/* Linux-PAM :extensionsPamItemType = */
-/// app supplied function to override failure delays
-pub const PAM_FAIL_DELAY: PamItemType = 10;
-/// X :display name
-pub const PAM_XDISPLAY: PamItemType = 11;
-/// X :server authentication data
-pub const PAM_XAUTHDATA: PamItemType = 12;
-/// The type for pam_get_authtok
-pub const PAM_AUTHTOK_TYPE: PamItemType = 13;
-
-// 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,
-}
--- a/src/conv.rs	Sat Sep 23 14:30:18 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,81 +0,0 @@
-use libc::{c_char, c_int};
-use std::ptr;
-use std::ffi::{CStr, CString};
-
-use constants::PamResultCode;
-use constants::*;
-use module::{PamItem, PamResult};
-
-#[allow(missing_copy_implementations)]
-pub enum AppDataPtr {}
-
-#[repr(C)]
-struct PamMessage {
-    msg_style: PamMessageStyle,
-    msg: *const c_char,
-}
-
-#[repr(C)]
-struct PamResponse {
-    resp: *const c_char,
-    resp_retcode: AlwaysZero,
-}
-
-/// `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 PamConv {
-    conv: extern "C" fn(num_msg: c_int,
-                        pam_message: &&PamMessage,
-                        pam_response: &mut *const PamResponse,
-                        appdata_ptr: *const AppDataPtr)
-                        -> PamResultCode,
-    appdata_ptr: *const AppDataPtr,
-}
-
-impl PamConv {
-    /// 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<String>> {
-        let mut resp_ptr: *const PamResponse = ptr::null();
-        let msg = PamMessage {
-            msg_style: style,
-            msg: CString::new(msg).unwrap().as_ptr(),
-        };
-
-        let ret = (self.conv)(1, &&msg, &mut resp_ptr, self.appdata_ptr);
-
-        if PamResultCode::PAM_SUCCESS == ret {
-            if resp_ptr.is_null() {
-                Ok(None)
-            } else {
-                let bytes = unsafe { CStr::from_ptr((*resp_ptr).resp).to_bytes() };
-                Ok(String::from_utf8(bytes.to_vec()).ok())
-            }
-        } else {
-            Err(ret)
-        }
-    }
-}
-
-impl PamItem for PamConv {
-    fn item_type() -> PamItemType {
-        PAM_CONV
-    }
-}
--- a/src/items.rs	Sat Sep 23 14:30:18 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-use constants::{PamItemType, PAM_SERVICE, PAM_USER, PAM_USER_PROMPT, PAM_TTY, PAM_RUSER, PAM_RHOST,
-                PAM_AUTHTOK, PAM_OLDAUTHTOK};
-use module::PamItem;
-pub use conv::PamConv;
-
-
-pub struct PamService {}
-
-impl PamItem for PamService {
-    fn item_type() -> PamItemType {
-        PAM_SERVICE
-    }
-}
-
-pub struct PamUser {}
-
-impl PamItem for PamUser {
-    fn item_type() -> PamItemType {
-        PAM_USER
-    }
-}
-
-pub struct PamUserPrompt {}
-
-impl PamItem for PamUserPrompt {
-    fn item_type() -> PamItemType {
-        PAM_USER_PROMPT
-    }
-}
-
-pub struct PamTty {}
-
-impl PamItem for PamTty {
-    fn item_type() -> PamItemType {
-        PAM_TTY
-    }
-}
-
-pub struct PamRUser {}
-
-impl PamItem for PamRUser {
-    fn item_type() -> PamItemType {
-        PAM_RUSER
-    }
-}
-
-pub struct PamRHost {}
-
-impl PamItem for PamRHost {
-    fn item_type() -> PamItemType {
-        PAM_RHOST
-    }
-}
-
-pub struct PamAuthTok {}
-
-impl PamItem for PamAuthTok {
-    fn item_type() -> PamItemType {
-        PAM_AUTHTOK
-    }
-}
-
-pub struct PamOldAuthTok {}
-
-impl PamItem for PamOldAuthTok {
-    fn item_type() -> PamItemType {
-        PAM_OLDAUTHTOK
-    }
-}
--- a/src/lib.rs	Sat Sep 23 14:30:18 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,32 +0,0 @@
-//! 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 conv;
-pub mod constants;
-pub mod items;
-pub mod module;
--- a/src/module.rs	Sat Sep 23 14:30:18 2017 -0600
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,179 +0,0 @@
-//! Functions for use in pam modules.
-
-use libc::c_char;
-use std::{mem, ptr};
-use std::ffi::{CStr, CString};
-
-use constants::{PamResultCode, PamItemType};
-
-/// 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.
-#[allow(missing_copy_implementations)]
-pub enum PamHandleT {}
-
-#[allow(missing_copy_implementations)]
-enum PamItemT {}
-
-#[allow(missing_copy_implementations)]
-pub enum PamDataT {}
-
-#[link(name = "pam")]
-extern "C" {
-    fn pam_get_data(pamh: *const PamHandleT,
-                    module_data_name: *const c_char,
-                    data: &mut *const PamDataT)
-                    -> PamResultCode;
-
-    fn pam_set_data(pamh: *const PamHandleT,
-                    module_data_name: *const c_char,
-                    data: Box<PamDataT>,
-                    cleanup: extern "C" fn(pamh: *const PamHandleT,
-                                           data: Box<PamDataT>,
-                                           error_status: PamResultCode))
-                    -> PamResultCode;
-
-    fn pam_get_item(pamh: *const PamHandleT,
-                    item_type: PamItemType,
-                    item: &mut *const PamItemT)
-                    -> PamResultCode;
-
-    fn pam_set_item(pamh: *mut PamHandleT,
-                    item_type: PamItemType,
-                    item: &PamItemT)
-                    -> PamResultCode;
-
-    fn pam_get_user(pamh: *const PamHandleT,
-                    user: &*mut c_char,
-                    prompt: *const c_char)
-                    -> PamResultCode;
-}
-
-pub type PamResult<T> = Result<T, PamResultCode>;
-
-/// Type-level mapping for safely retrieving values with `get_item`.
-///
-/// See `pam_get_item` in
-/// http://www.linux-pam.org/Linux-PAM-html/mwg-expected-by-module-item.html
-pub trait PamItem {
-    /// Maps a Rust type to a pam constant.
-    ///
-    /// For example, the type PamConv maps to the constant PAM_CONV.  The pam
-    /// API contract specifies that when the API function `pam_get_item` is
-    /// called with the constant PAM_CONV, it will return a value of type
-    /// `PamConv`.
-    fn item_type() -> PamItemType;
-}
-
-/// 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
-pub unsafe fn get_data<'a, T>(pamh: &'a PamHandleT, key: &str) -> PamResult<&'a T> {
-    let c_key = CString::new(key).unwrap().as_ptr();
-    let mut ptr: *const PamDataT = ptr::null();
-    let res = pam_get_data(pamh, c_key, &mut ptr);
-    if PamResultCode::PAM_SUCCESS == res && !ptr.is_null() {
-        let typed_ptr: *const T = mem::transmute(ptr);
-        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
-pub fn set_data<T>(pamh: &PamHandleT, key: &str, data: Box<T>) -> PamResult<()> {
-    let c_key = CString::new(key).unwrap().as_ptr();
-    let res = unsafe {
-        let c_data: Box<PamDataT> = mem::transmute(data);
-        pam_set_data(pamh, c_key, c_data, cleanup::<T>)
-    };
-    if PamResultCode::PAM_SUCCESS == res {
-        Ok(())
-    } else {
-        Err(res)
-    }
-}
-
-#[no_mangle]
-pub extern "C" fn cleanup<T>(_: *const PamHandleT, c_data: Box<PamDataT>, _: PamResultCode) {
-    unsafe {
-        let data: Box<T> = mem::transmute(c_data);
-        mem::drop(data);
-    }
-}
-
-/// 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
-pub fn get_item<'a, T: PamItem>(pamh: &'a PamHandleT) -> PamResult<&'a T> {
-    let mut ptr: *const PamItemT = ptr::null();
-    let (res, item) = unsafe {
-        let r = pam_get_item(pamh, T::item_type(), &mut ptr);
-        let typed_ptr: *const T = mem::transmute(ptr);
-        let t: &T = &*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
-pub fn set_item_str<'a, T: PamItem>(pamh: &'a mut PamHandleT, item: &str) -> PamResult<()> {
-    let c_item = CString::new(item).unwrap().as_ptr();
-
-    let res = unsafe {
-        pam_set_item(pamh,
-                     T::item_type(),
-
-                     // unwrapping is okay here, as c_item will not be a NULL
-                     // pointer
-                     (c_item as *const PamItemT).as_ref().unwrap())
-    };
-    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
-pub fn get_user<'a>(pamh: &'a PamHandleT, prompt: Option<&str>) -> PamResult<String> {
-    let ptr: *mut c_char = ptr::null_mut();
-    let c_prompt = match prompt {
-        Some(p) => CString::new(p).unwrap().as_ptr(),
-        None => ptr::null(),
-    };
-    let res = unsafe { pam_get_user(pamh, &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)
-    }
-}