pam-0.7.0/.gitignore010064400017500000144000000002271321000507400124710ustar0000000000000000# Compiled files *.o *.so *.rlib *.dll # Executables *.exe # Generated by Cargo /target/ # Ignore Cargo.lock since pam-auth is a library Cargo.lock pam-0.7.0/.travis.yml010064400017500000144000000004441324603467100126310ustar0000000000000000language: rust cache: cargo rust: - stable - beta - nightly matrix: allow_failures: - rust: nightly sudo: false addons: apt: packages: - libpam0g-dev script: - cargo build --release --verbose - cargo test --verbose notifications: email: on_success: never pam-0.7.0/CHANGELOG.md010064400017500000144000000075451331723017100123320ustar0000000000000000# Changelog All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). ## [Unreleased] ## [0.5.5] - 2018-07-04 ### Changed - Updated `libc` dependency (0.2.39 -> 0.2.42) - Updated `pam-sys` dependency (0.5.5 -> 0.5.6) ## [0.5.4] - 2018-03-21 ### Changed - Updated `libc` dependency (0.2.33 -> 0.2.39) - Updated `pam-sys` dependency (0.5.4 -> 0.5.5) - Updated `users` dependency (0.5.2 -> 0.5.3) ### Fixed - Fix build on OSX and linux-arm ## [0.5.3] - 2017-12-04 ### Changed - Only provide official support for Rust stable, beta and nightly (mainly through travis) - Updated `libc` dependency (0.2.20 -> 0.2.33) - Updated `pam-sys` dependency (0.5.3 -> 0.5.4) - Addded `cache: cargo` directive to speedup CI ## [0.5.2] - 2017-06-19 ### Fixed - Fixed missing null terminations in PAM `converse` function (Pull request #6) ## [0.5.1] - 2017-05-25 ### Fixed - Removed use of unstable feature `ptr_as_ref` to build on 1.5.0 again ## [0.5.0] - 2017-02-18 ### Added - Add travis-ci badge to `Cargo.toml` - Added custom error type `PamError` and result type - Added `env` module for PAM environment modules ### Changed - Moved `Authenticator` to its own module - Removed custom `strdup` function in `ffi` and replaced it with the `libc` version - Tracked `pam-sys` - Use of Rust types where applicable - Removal of obsolete `unsafe` blocks - Changed `Authenticator::open_session` to also insert the PAM environment variables into the process environment ## [0.4.1] 2017-01-20 ### Added - Added license badge to `README.md` ### Changed - Updated `libc` dependency (0.2.9 -> 0.2.20) - Updated `pam-sys` dependency (0.4.0 -> 0.4.3) - Updated `users` dependency (0.5.1 -> 0.5.2) - Moved call to `pam_setcred` from `Authenticator::authenticate()` to `Authenticator::open_session` - Moved documentation to [docs.rs](https://docs.rs/pam-auth/) ### Fixed - Fixed possibly undefined behaviour (taking a pointer of a dropped `CString`) in `Authenticator::new(..)` ### Removed - Removed `.travis-update-gh-pages.sh` and obsolete rust versions from `.travis.yml` ## [0.4.0] - 2016-04-11 ### Changed - Improved travis-ci integration to test against 1.5.0 and above - Updated `libc` dependency (0.2.2 -> 0.2.9) - Updated `pam-sys` dependency (0.3.0 -> 0.4.0) - Updated `users` dependency (0.4.4 -> 0.5.1) ## [0.3.1] - 2016-01-14 ### Changed - Relicensed to dual MIT/Apache-2.0 - Improved travis-ci integration to use container based builds ## [0.3.0] - 2015-12-08 ### Added - CHANGELOG.md - `pam-auth` now builds on Rust stable (and beta)! - Better travis-ci integration (test on stable, beta and nightly) ### Changed - Updated `libc` dependency (0.1.8 -> 0.2.2) - Updated `pam-sys` dependency (0.2.1 -> 0.3.0) - Updated `users` dependency (0.4.2 -> 0.4.4) [Unreleased]: https://github.com/1wilkens/pam-auth/compare/v0.5.5...HEAD [0.5.5]: https://github.com/1wilkens/pam-auth/compare/v0.5.4...v0.5.5 [0.5.3]: https://github.com/1wilkens/pam-auth/compare/v0.5.3...v0.5.4 [0.5.3]: https://github.com/1wilkens/pam-auth/compare/v0.5.2...v0.5.3 [0.5.2]: https://github.com/1wilkens/pam-auth/compare/v0.5.1...v0.5.2 [0.5.1]: https://github.com/1wilkens/pam-auth/compare/v0.5.0...v0.5.1 [0.5.0]: https://github.com/1wilkens/pam-auth/compare/v0.4.1...v0.5.0 [0.4.1]: https://github.com/1wilkens/pam-auth/compare/v0.4.0...v0.4.1 [0.4.0]: https://github.com/1wilkens/pam-auth/compare/v0.3.1...v0.4.0 [0.3.1]: https://github.com/1wilkens/pam-auth/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/1wilkens/pam-auth/compare/v0.2.0...v0.3.0 pam-0.7.0/Cargo.toml.orig010064400017500000144000000007611342712614700134120ustar0000000000000000[package] name = "pam" version = "0.7.0" authors = ["Florian Wilkens "] description = "Safe Rust wrappers for PAM authentification" repository = "https://github.com/1wilkens/pam/" documentation = "https://docs.rs/pam/" readme = "README.md" license = "MIT OR Apache-2.0" categories = ["authentication"] edition = "2018" [badges] travis-ci = { repository = "1wilkens/pam" } [dependencies] libc = "^0.2" pam-sys = "^0.5" users = "^0.8" [dev-dependencies] rpassword = "2.0" pam-0.7.0/Cargo.toml0000644000000020550000000000000076540ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g. crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "pam" version = "0.7.0" authors = ["Florian Wilkens "] description = "Safe Rust wrappers for PAM authentification" documentation = "https://docs.rs/pam/" readme = "README.md" categories = ["authentication"] license = "MIT OR Apache-2.0" repository = "https://github.com/1wilkens/pam/" [dependencies.libc] version = "^0.2" [dependencies.pam-sys] version = "^0.5" [dependencies.users] version = "^0.8" [dev-dependencies.rpassword] version = "2.0" [badges.travis-ci] repository = "1wilkens/pam" pam-0.7.0/LICENSE-APACHE010064400017500000144000000250261342711161300124400ustar0000000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 2015-2019 The pam-auth developers 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. pam-0.7.0/LICENSE-MIT010064400017500000144000000020601342711161300121410ustar0000000000000000Copyright (c) 2015-2019 The pam-auth developers 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. pam-0.7.0/README.md010064400017500000144000000050171342712614700120010ustar0000000000000000# pam [![Version](https://img.shields.io/crates/v/pam.svg)](https://crates.io/crates/pam) [![License](https://img.shields.io/crates/l/pam.svg?branch=master)](https://travis-ci.org/1wilkens/pam) [![Build Status](https://travis-ci.org/1wilkens/pam.svg)](https://travis-ci.org/1wilkens/pam) Safe Rust bindings to Linux Pluggable Authentication Modules (PAM). Currently only supports basic username/password authentication out-of-the-box. [Documentation @ docs.rs](https://docs.rs/pam/) ## Warning Environment support through the `env` module is probably broken and should not be used in the current state! ## Supported Rust versions The library is only continuously built against Rust stable, beta and nightly but as it does not use a lot of new language features it should probably compile on older versions as well. If you encounter problems building on older versions and a small fix can be applied to make the build succeed, consider opening a pull request. ## Note about stability This crate follows [semantic versioning](http://semver.org). As such all versions below `1.0.0` should be considered development versions. This means the API could change any time. ## Usage 1. Add `pam-auth` to your Cargo.toml: ```toml [dependencies] pam = "0.7.0" ``` 2. Use the `Authenticator` struct to authenticate and open a session ```rust extern crate pam; pub fn main() { let service = ""; let user = ""; let password = ""; let mut auth = pam::Authenticator::with_password(service).unwrap(); auth.get_handler().set_credentials(user, password); if auth.authenticate().is_ok() && auth.open_session().is_ok() { println!("Successfully opened a session!"); } else { println!("Authentication failed =/"); } } ``` ## TODO: - [x] Implement basic user/password authentication - [x] Add `Authenticator` struct - [ ] Add (more) documentation - [x] Verify current `conv` does not leak memory - [x] Allow custom `conv` functions to be passed - [ ] Code cleanup ## License Licensed under either of * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ### Contribution Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. pam-0.7.0/examples/spawn_bash.rs010064400017500000144000000031341342712421100150170ustar0000000000000000use std::io::{stdin, stdout, Write}; use std::os::unix::process::CommandExt; use std::process::Command; use pam_auth::Authenticator; use users::get_user_by_name; use rpassword::read_password_from_tty; // A simple program that requests a login and a password and then spawns /bin/bash as the // user who logged in. // // Note that this proto-"sudo" is very insecure and should not be used in any production setup, // it is just an example to show how the PAM api works. fn main() { // First, prompt the user for a login and a password print!("login: "); stdout().flush().unwrap(); let mut login = String::new(); stdin().read_line(&mut login).unwrap(); login.pop(); // remove the trailing '\n' let password = read_password_from_tty(Some("password: ")).unwrap(); // Now, setup the authenticator, we require the basic "system-auth" service let mut authenticator = Authenticator::with_password("system-auth").expect("Failed to init PAM client!"); authenticator.get_handler().set_credentials(login.clone(), password); authenticator.authenticate().expect("Authentication failed!"); authenticator.open_session().expect("Failed to open a session!"); // we now try to spawn `/bin/bash` as this user // note that setting the uid/gid is likely to fail if this program is not already run as the // proper user or as root let user = get_user_by_name(&login).unwrap(); let error = Command::new("/bin/bash").uid(user.uid()).gid(user.primary_group_id()).exec(); // if exec() returned, this means there was an error: println!("Error spawning bash: {:?}", error); } pam-0.7.0/src/authenticator.rs010064400017500000144000000165411342712614700145350ustar0000000000000000use pam_sys::{ acct_mgmt, authenticate, close_session, end, getenv, open_session, putenv, setcred, start, }; use pam_sys::{PamFlag, PamHandle, PamReturnCode}; use users; use std::{env, ptr}; use crate::{env::get_pam_env, ffi, Converse, PamError, PamResult, PasswordConv}; /// Main struct to authenticate a user /// /// You need to create an instance of it to start an authentication process. If you /// want a simple password-based authentication, you can use `Authenticator::with_password`, /// and to the following flow: /// /// ```no_run /// use pam_auth::Authenticator; /// /// let mut authenticator = Authenticator::with_password("system-auth") /// .expect("Failed to init PAM client."); /// // Preset the login & password we will use for authentication /// authenticator.get_handler().set_credentials("login", "password"); /// // actually try to authenticate: /// authenticator.authenticate().expect("Authentication failed!"); /// // Now that we are authenticated, it's possible to open a sesssion: /// authenticator.open_session().expect("Failed to open a session!"); /// ``` /// /// If you wish to customise the PAM conversation function, you should rather create your /// authenticator with `Authenticator::with_handler`, providing a struct implementing the /// `Converse` trait. You can then mutably access your conversation handler using the /// `Authenticator::get_handler` method. /// /// By default, the `Authenticator` will close any opened session when dropped. If you don't /// want this, you can change its `close_on_drop` field to `False`. pub struct Authenticator<'a, C: Converse> { /// Flag indicating whether the Authenticator should close the session on drop pub close_on_drop: bool, handle: &'a mut PamHandle, converse: Box, is_authenticated: bool, has_open_session: bool, last_code: PamReturnCode, } impl<'a> Authenticator<'a, PasswordConv> { /// Create a new `Authenticator` with a given service name and a password-based conversation pub fn with_password(service: &str) -> PamResult> { Authenticator::with_handler(service, PasswordConv::new()) } } impl<'a, C: Converse> Authenticator<'a, C> { /// Creates a new Authenticator with a given service name and conversation callback pub fn with_handler(service: &str, converse: C) -> PamResult> { let mut converse = Box::new(converse); let conv = ffi::make_conversation(&mut *converse); let mut handle: *mut PamHandle = ptr::null_mut(); match start(service, None, &conv, &mut handle) { PamReturnCode::SUCCESS => unsafe { Ok(Authenticator { close_on_drop: true, handle: &mut *handle, converse, is_authenticated: false, has_open_session: false, last_code: PamReturnCode::SUCCESS, }) }, code => Err(PamError(code)), } } /// Access the conversation handler of this Authenticator pub fn get_handler(&mut self) -> &mut C { &mut *self.converse } /// Perform the authentication with the provided credentials pub fn authenticate(&mut self) -> PamResult<()> { self.last_code = authenticate(self.handle, PamFlag::NONE); if self.last_code != PamReturnCode::SUCCESS { // No need to reset here return Err(From::from(self.last_code)); } self.is_authenticated = true; self.last_code = acct_mgmt(self.handle, PamFlag::NONE); if self.last_code != PamReturnCode::SUCCESS { // Probably not strictly neccessary but better be sure return self.reset(); } Ok(()) } /// Open a session for a previously authenticated user and /// initialize the environment appropriately (in PAM and regular enviroment variables). pub fn open_session(&mut self) -> PamResult<()> { if !self.is_authenticated { //TODO: is this the right return code? return Err(PamReturnCode::PERM_DENIED.into()); } self.last_code = setcred(self.handle, PamFlag::ESTABLISH_CRED); if self.last_code != PamReturnCode::SUCCESS { return self.reset(); } self.last_code = open_session(self.handle, PamFlag::NONE); if self.last_code != PamReturnCode::SUCCESS { return self.reset(); } // Follow openSSH and call pam_setcred before and after open_session self.last_code = setcred(self.handle, PamFlag::REINITIALIZE_CRED); if self.last_code != PamReturnCode::SUCCESS { return self.reset(); } self.has_open_session = true; self.initialize_environment() } // Initialize the client environment with common variables. // Currently always called from Authenticator.open_session() fn initialize_environment(&mut self) -> PamResult<()> { use users::os::unix::UserExt; // Set PAM environment in the local process if let Some(mut env_list) = get_pam_env(self.handle) { let env = env_list.to_vec(); for (key, value) in env { env::set_var(&key, &value); } } let user = users::get_user_by_name(self.converse.username()).unwrap_or_else(|| { panic!("Could not get user by name: {:?}", self.converse.username()) }); // Set some common environment variables self.set_env( "USER", user.name() .to_str() .expect("Unix usernames should be valid UTF-8"), )?; self.set_env( "LOGNAME", user.name() .to_str() .expect("Unix usernames should be valid UTF-8"), )?; self.set_env("HOME", user.home_dir().to_str().unwrap())?; self.set_env("PWD", user.home_dir().to_str().unwrap())?; self.set_env("SHELL", user.shell().to_str().unwrap())?; // Taken from https://github.com/gsingh93/display-manager/blob/master/pam.c // Should be a better way to get this. Revisit later. self.set_env("PATH", "$PATH:/usr/local/sbin:/usr/local/bin:/usr/bin")?; Ok(()) } // Utility function to set an environment variable in PAM and the process fn set_env(&mut self, key: &str, value: &str) -> PamResult<()> { // Set regular environment variable env::set_var(key, value); // Set pam environment variable if getenv(self.handle, key).is_none() { let name_value = format!("{}={}", key, value); match putenv(self.handle, &name_value) { PamReturnCode::SUCCESS => Ok(()), code => Err(From::from(code)), } } else { Ok(()) } } // Utility function to reset the pam handle in case of intermediate errors fn reset(&mut self) -> PamResult<()> { setcred(self.handle, PamFlag::DELETE_CRED); self.is_authenticated = false; Err(From::from(self.last_code)) } } impl<'a, C: Converse> Drop for Authenticator<'a, C> { fn drop(&mut self) { if self.has_open_session && self.close_on_drop { close_session(self.handle, PamFlag::NONE); } let code = setcred(self.handle, PamFlag::DELETE_CRED); end(self.handle, code); } } pam-0.7.0/src/env.rs010064400017500000144000000024461342712614700124520ustar0000000000000000use libc::c_char; use pam_sys::{getenvlist, raw, PamHandle}; use std::ffi::CStr; pub struct PamEnvList { ptr: *const *const c_char, } pub fn get_pam_env(handle: &mut PamHandle) -> Option { let env = getenvlist(handle); if !env.is_null() { Some(PamEnvList { ptr: env }) } else { None } } impl PamEnvList { pub fn to_vec(&mut self) -> Vec<(String, String)> { let mut vec = Vec::new(); let mut idx = 0; loop { let env_ptr: *const *const c_char = unsafe { self.ptr.offset(idx) }; if unsafe { !(*env_ptr).is_null() } { idx += 1; let env = unsafe { CStr::from_ptr(*env_ptr) }.to_string_lossy(); let split: Vec<_> = env.splitn(2, '=').collect(); if split.len() == 2 { // Only add valid env vars (contain at least one '=') vec.push((split[0].into(), split[1].into())); } } else { // Reached the end of the env array -> break out of the loop break; } } vec } } #[cfg(target_os = "linux")] impl Drop for PamEnvList { fn drop(&mut self) { unsafe { raw::pam_misc_drop_env(self.ptr as *mut *mut c_char) }; } } pam-0.7.0/src/ffi.rs010064400017500000144000000047351342712614700124310ustar0000000000000000use libc::{c_int, c_void, calloc, free, size_t, strdup}; use pam_sys::{PamConversation, PamMessage, PamMessageStyle, PamResponse, PamReturnCode}; use std::ffi::CStr; use std::mem; use crate::Converse; pub fn make_conversation(user_converse: &mut C) -> PamConversation { PamConversation { conv: Some(converse::), data_ptr: user_converse as *mut C as *mut c_void, } } pub extern "C" fn converse( num_msg: c_int, msg: *mut *mut PamMessage, out_resp: *mut *mut PamResponse, appdata_ptr: *mut c_void, ) -> c_int { // allocate space for responses let resp = unsafe { calloc(num_msg as usize, mem::size_of::() as size_t) as *mut PamResponse }; if resp.is_null() { return PamReturnCode::BUF_ERR as c_int; } let handler = unsafe { &mut *(appdata_ptr as *mut C) }; let mut result: PamReturnCode = PamReturnCode::SUCCESS; for i in 0..num_msg as isize { unsafe { // get indexed values let m: &mut PamMessage = &mut **(msg.offset(i)); let r: &mut PamResponse = &mut *(resp.offset(i)); let msg = CStr::from_ptr(m.msg); // match on msg_style match PamMessageStyle::from(m.msg_style) { PamMessageStyle::PROMPT_ECHO_ON => { if let Ok(handler_response) = handler.prompt_echo(msg) { r.resp = strdup(handler_response.as_ptr()); } else { result = PamReturnCode::CONV_ERR; } } PamMessageStyle::PROMPT_ECHO_OFF => { if let Ok(handler_response) = handler.prompt_blind(msg) { r.resp = strdup(handler_response.as_ptr()); } else { result = PamReturnCode::CONV_ERR; } } PamMessageStyle::ERROR_MSG => { handler.error(msg); result = PamReturnCode::CONV_ERR; } PamMessageStyle::TEXT_INFO => { handler.info(msg); } } } if result != PamReturnCode::SUCCESS { break; } } // free allocated memory if an error occured if result != PamReturnCode::SUCCESS { unsafe { free(resp as *mut c_void) }; } else { unsafe { *out_resp = resp }; } result as c_int } pam-0.7.0/src/lib.rs010064400017500000144000000070411342712614700124240ustar0000000000000000extern crate pam_sys; mod authenticator; mod env; mod ffi; use pam_sys::PamReturnCode; use std::ffi::{CStr, CString}; pub use crate::authenticator::*; pub struct PamError(PamReturnCode); pub type PamResult = std::result::Result; impl std::fmt::Debug for PamError { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { self.0.fmt(fmt) } } impl std::fmt::Display for PamError { fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { self.0.fmt(fmt) } } impl std::error::Error for PamError { fn description(&self) -> &str { "PAM returned an error code" } } impl From for PamError { fn from(err: PamReturnCode) -> PamError { PamError(err) } } /// A trait representing the PAM authentification conversation /// /// PAM authentification is done as a conversation mechanism, in which PAM /// asks several questions and the client (your code) answers them. This trait /// is a representation of such a conversation, which one method for each message /// PAM can send you. /// /// This is the trait to implement if you want to customize the conversation with /// PAM. If you just want a simple login/password authentication, you can use the /// `PasswordConv` implementation provided by this crate. pub trait Converse { /// PAM requests a value that should be echoed to the user as they type it /// /// This would typically be the username. The exact question is provided as the /// `msg` argument if you wish to display it to your user. fn prompt_echo(&mut self, msg: &CStr) -> ::std::result::Result; /// PAM requests a value that should be typed blindly by the user /// /// This would typically be the password. The exact question is provided as the /// `msg` argument if you wish to display it to your user. fn prompt_blind(&mut self, msg: &CStr) -> ::std::result::Result; /// This is an informational message from PAM fn info(&mut self, msg: &CStr); /// This is an error message from PAM fn error(&mut self, msg: &CStr); /// Get the username that is being authenticated /// /// This method is not a PAM callback, but is rather used by the `Authenticator` to /// setup the environment when opening a session. fn username(&self) -> &str; } /// A minimalistic conversation handler, that uses given login and password /// /// This conversation handler is not really interactive, but simply returns to /// PAM the value that have been set using the `set_credentials` method. pub struct PasswordConv { login: String, passwd: String, } impl PasswordConv { /// Create a new `PasswordConv` handler fn new() -> PasswordConv { PasswordConv { login: String::new(), passwd: String::new(), } } /// Set the credentials that this handler will provide to PAM pub fn set_credentials, V: Into>(&mut self, login: U, password: V) { self.login = login.into(); self.passwd = password.into(); } } impl Converse for PasswordConv { fn prompt_echo(&mut self, _msg: &CStr) -> Result { CString::new(self.login.clone()).map_err(|_| ()) } fn prompt_blind(&mut self, _msg: &CStr) -> Result { CString::new(self.passwd.clone()).map_err(|_| ()) } fn info(&mut self, _msg: &CStr) {} fn error(&mut self, msg: &CStr) { eprintln!("[PAM ERROR] {}", msg.to_string_lossy()); } fn username(&self) -> &str { &self.login } } pam-0.7.0/.cargo_vcs_info.json0000644000000001120000000000000116460ustar00{ "git": { "sha1": "27f18c37b521603417717ca354edc7fc070315d2" } }