Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Get started on better implementation handling #25

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ azure-devops = { project = "1wilkens/ci", pipeline = "pam-sys" }
libc = "^0.2.65"

[build-dependencies]
bindgen = "0.59"
bindgen = "0.61"
206 changes: 141 additions & 65 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,74 +3,150 @@ extern crate bindgen;
use std::env;
use std::path::PathBuf;

fn main() {
// Tell cargo to tell rustc to link the system pam
// shared library.
println!("cargo:rustc-link-lib=pam");
const PAM_IMPL_ENV_VAR: &str = "USE_PAM_IMPL";

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum PamImplementation {
LinuxPam,
OpenPam,
}

impl PamImplementation {
fn resolve() -> Self {
if let Ok(pam_imp) = env::var(PAM_IMPL_ENV_VAR) {
let pam_impl = pam_imp.to_lowercase();
return match &pam_impl[..] {
"linuxpam" => Self::LinuxPam,
"openpam" => Self::OpenPam,
_ => {
panic!("Unrecognized '{}' environment variable value '{}'. Assessing from other information.", PAM_IMPL_ENV_VAR, pam_impl);
}
};
}

// LinuxPAM is used by linux and android
if cfg!(target_os = "linux") || cfg!(target_os = "android") {
Self::LinuxPam
} else if cfg!(target_os = "freebsd")
|| cfg!(target_os = "netbsd")
|| cfg!(target_os = "macos")
|| cfg!(target_os = "ios")
|| cfg!(target_os = "dragonfly")
{
Self::OpenPam
} else {
panic!("Failed to resolve the PAM implementation. Use an appropriate target platform or set the `{}` environment variable to either `LINUXPAM` or `OPENPAM`.", PAM_IMPL_ENV_VAR);
}
}

// pam_misc is only supported on Linux afaik
if cfg!(target_os = "linux") {
println!("cargo:rustc-link-lib=pam_misc");
fn impl_name(self) -> &'static str {
match self {
Self::LinuxPam => "linux-pam",
Self::OpenPam => "openpam",
}
}

// Tell cargo to invalidate the built crate whenever the wrapper changes
println!("cargo:rerun-if-changed=wrapper.h");

// Prepare bindgen builder
let mut builder = bindgen::Builder::default()
// Our header
.header("wrapper.h")
// Use libc for c-types
.ctypes_prefix("libc")
// pam_handle_t is opaque
.opaque_type("pam_handle_t")
// Block varargs functions and related types for now
// TODO: find a nice solution for this
.blocklist_type("va_list")
.blocklist_type("__va_list")
.blocklist_type("__builtin_va_list")
.blocklist_type("__va_list_tag")
.blocklist_function("pam_v.*")
.blocklist_function("pam_syslog")
.blocklist_function("pam_prompt")
// Allow all PAM constants
.allowlist_var("PAM_.*")
// Allow all PAM functions..
.allowlist_function("pam_.*")
// ..except module related functions (pam_sm_*)
.blocklist_function("pam_sm_.*");

// Platform-specific adaptions
if cfg!(target_os = "linux") {
builder = builder
// Set macro constants to signed int, as all functions that accept
// these constants use signed int as the parameter type
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
//
// Use libc types so our signatures are slightly nicer
.raw_line("use libc::{uid_t, gid_t, group, passwd, spwd};")
.blocklist_type(".*gid_t")
.blocklist_type(".*uid_t")
.blocklist_type("group")
.blocklist_type("passwd")
.blocklist_type("spwd");
} else if cfg!(target_os = "freebsd") || cfg!(target_os = "netbsd") {
// XXX: this should include all OS that use openPAM
builder = builder
// Use libc types so our signatures are slightly nicer
.raw_line("use libc::passwd;")
.blocklist_type("passwd");
fn get_additional_libs(self) -> &'static [&'static str] {
match self {
Self::LinuxPam => &["pam_misc"],
Self::OpenPam => &[],
}
}

let bindings = builder
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");

// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.expect("Couldn't write bindings!");
fn iter() -> std::slice::Iter<'static, PamImplementation> {
[Self::LinuxPam, Self::OpenPam].iter()
}

fn write_bindings(self, is_linked: bool) {
let base_folder = if is_linked { "wrappers" } else { "reflibs" };
let impl_name = self.impl_name();

let builder = bindgen::Builder::default()
// Our header
.header(&format!("{base_folder}/{impl_name}.h"))
// Use libc for c-types
.ctypes_prefix("libc")
// pam_handle_t is opaque
.opaque_type("pam_handle_t")
// Block varargs functions and related types for now
// TODO: find a nice solution for this
.blocklist_type("va_list")
.blocklist_type("__va_list")
.blocklist_type("__builtin_va_list")
.blocklist_type("__va_list_tag")
.blocklist_function("pam_v.*")
.blocklist_function("pam_syslog")
.blocklist_function("pam_prompt")
// Allow all PAM constants
.allowlist_var("PAM_.*")
// Allow all PAM functions..
.allowlist_function("pam_.*");

let builder = match self {
Self::LinuxPam => {
builder
// Tell cargo to invalidate the built crate whenever the wrapper changes
// Set macro constants to signed int, as all functions that accept these constants use
// signed int as the parameter type
.default_macro_constant_type(bindgen::MacroTypeVariation::Signed)
// Use libc types so our signatures are slightly nicer
.raw_line("use libc::{uid_t, gid_t, group, passwd, spwd};")
.blocklist_type(".*gid_t")
.blocklist_type(".*uid_t")
.blocklist_type("group")
.blocklist_type("passwd")
.blocklist_type("spwd")
}
Self::OpenPam => {
builder
// Use libc types so our signatures are slightly nicer
.raw_line("use libc::passwd;")
.blocklist_type("passwd")
// Allow all PAM constants
.allowlist_var("OPENPAM_.*")
// Allow all PAM functions..
.allowlist_function("openpam_.*")
}
};

let bindings = builder
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");

// Write the bindings to the $OUT_DIR/bindings-openpam.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join(&format!("bindings-{impl_name}.rs")))
.expect("Couldn't write bindings!");
}
}

fn main() {
let pam_implementation = PamImplementation::resolve();
println!(
"cargo:rustc-cfg=pam_sys_pam_impl=\"{impl_name}\"",
impl_name = pam_implementation.impl_name()
);

// Tell cargo to tell rustc to link the system pam shared library.
println!("cargo:rustc-link-lib=pam");
for additional_lib in pam_implementation.get_additional_libs() {
println!("cargo:rustc-link-lib={additional_lib}",);
}

println!("cargo:rerun-if-changed=wrappers");
println!("cargo:rerun-if-changed=reflibs");

pam_implementation.write_bindings(true);

for other_impl in PamImplementation::iter() {
if *other_impl == pam_implementation {
continue;
}

println!("cargo:warning=Generating bindings for {other_impl:?}");
other_impl.write_bindings(false);
}
}
7 changes: 7 additions & 0 deletions reflibs/linux-pam.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "./linux-pam/pam_appl.h"
#include "./linux-pam/pam_client.h"
#include "./linux-pam/pam_ext.h"
#include "./linux-pam/pam_filter.h"
#include "./linux-pam/pam_misc.h"
#include "./linux-pam/pam_modules.h"
#include "./linux-pam/pam_modutil.h"
126 changes: 126 additions & 0 deletions reflibs/linux-pam/_pam_compat.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
#ifndef _PAM_COMPAT_H
#define _PAM_COMPAT_H

/*
* This file was contributed by Derrick J Brashear <shadow@dementia.org>
* slight modification by Brad M. Garcia <bgarcia@fore.com>
*
* A number of operating systems have started to implement PAM.
* unfortunately, they have a different set of numeric values for
* certain constants. This file is included for compatibility's sake.
*/

/* Solaris uses different constants. We redefine to those here */
#if defined(solaris) || (defined(__SVR4) && defined(sun))

# ifdef _SECURITY_PAM_MODULES_H

/* flags for pam_chauthtok() */
# undef PAM_PRELIM_CHECK
# define PAM_PRELIM_CHECK 0x1

# undef PAM_UPDATE_AUTHTOK
# define PAM_UPDATE_AUTHTOK 0x2

# endif /* _SECURITY_PAM_MODULES_H */

# ifdef _SECURITY__PAM_TYPES_H

/* generic for pam_* functions */
# undef PAM_SILENT
# define PAM_SILENT 0x80000000

# undef PAM_CHANGE_EXPIRED_AUTHTOK
# define PAM_CHANGE_EXPIRED_AUTHTOK 0x4

/* flags for pam_setcred() */
# undef PAM_ESTABLISH_CRED
# define PAM_ESTABLISH_CRED 0x1

# undef PAM_DELETE_CRED
# define PAM_DELETE_CRED 0x2

# undef PAM_REINITIALIZE_CRED
# define PAM_REINITIALIZE_CRED 0x4

# undef PAM_REFRESH_CRED
# define PAM_REFRESH_CRED 0x8

/* another binary incompatibility comes from the return codes! */

# undef PAM_CONV_ERR
# define PAM_CONV_ERR 6

# undef PAM_PERM_DENIED
# define PAM_PERM_DENIED 7

# undef PAM_MAXTRIES
# define PAM_MAXTRIES 8

# undef PAM_AUTH_ERR
# define PAM_AUTH_ERR 9

# undef PAM_NEW_AUTHTOK_REQD
# define PAM_NEW_AUTHTOK_REQD 10

# undef PAM_CRED_INSUFFICIENT
# define PAM_CRED_INSUFFICIENT 11

# undef PAM_AUTHINFO_UNAVAIL
# define PAM_AUTHINFO_UNAVAIL 12

# undef PAM_USER_UNKNOWN
# define PAM_USER_UNKNOWN 13

# undef PAM_CRED_UNAVAIL
# define PAM_CRED_UNAVAIL 14

# undef PAM_CRED_EXPIRED
# define PAM_CRED_EXPIRED 15

# undef PAM_CRED_ERR
# define PAM_CRED_ERR 16

# undef PAM_ACCT_EXPIRED
# define PAM_ACCT_EXPIRED 17

# undef PAM_AUTHTOK_EXPIRED
# define PAM_AUTHTOK_EXPIRED 18

# undef PAM_SESSION_ERR
# define PAM_SESSION_ERR 19

# undef PAM_AUTHTOK_ERR
# define PAM_AUTHTOK_ERR 20

# undef PAM_AUTHTOK_RECOVERY_ERR
# define PAM_AUTHTOK_RECOVERY_ERR 21

# undef PAM_AUTHTOK_LOCK_BUSY
# define PAM_AUTHTOK_LOCK_BUSY 22

# undef PAM_AUTHTOK_DISABLE_AGING
# define PAM_AUTHTOK_DISABLE_AGING 23

# undef PAM_NO_MODULE_DATA
# define PAM_NO_MODULE_DATA 24

# undef PAM_IGNORE
# define PAM_IGNORE 25

# undef PAM_ABORT
# define PAM_ABORT 26

# undef PAM_TRY_AGAIN
# define PAM_TRY_AGAIN 27

#endif /* _SECURITY__PAM_TYPES_H */

#else

/* For compatibility with old Linux-PAM implementations. */
#define PAM_AUTHTOK_RECOVER_ERR PAM_AUTHTOK_RECOVERY_ERR

#endif /* defined(solaris) || (defined(__SVR4) && defined(sun)) */

#endif /* _PAM_COMPAT_H */
Loading