extern crate libc;
use libc::{c_char, c_void, c_int};
use std::ffi::{CStr, CString};
use std::ptr::{null, null_mut};
use std::path::Path;
static PWQ_SETTING_DIFF_OK: c_int = 1;
static PWQ_SETTING_MIN_LENGTH: c_int = 3;
static PWQ_SETTING_DIG_CREDIT: c_int = 4;
static PWQ_SETTING_UP_CREDIT: c_int = 5;
static PWQ_SETTING_LOW_CREDIT: c_int = 6;
static PWQ_SETTING_OTH_CREDIT: c_int = 7;
static PWQ_SETTING_MIN_CLASS: c_int = 8;
static PWQ_SETTING_MAX_REPEAT: c_int = 9;
static PWQ_SETTING_DICT_PATH: c_int = 10;
static PWQ_SETTING_MAX_CLASS_REPEAT: c_int = 11;
static PWQ_SETTING_GECOS_CHECK: c_int = 12;
static PWQ_SETTING_BAD_WORDS: c_int = 13;
static PWQ_SETTING_MAX_SEQUENCE: c_int = 14;
static PWQ_SETTING_DICT_CHECK: c_int = 15;
static PWQ_SETTING_USER_CHECK: c_int = 16;
static PWQ_SETTING_ENFORCING: c_int = 17;
#[derive(Clone, Debug)]
pub enum Error {
FatalFailure, Integer, CfgfileOpen, CfgfileMalformed, UnknownSetting, NonIntSetting, NonStrSetting, MemAlloc, TooSimilar, MinDigits, MinUppers, MinLowers, MinOthers, MinLength, Palindrome, CaseChangesOnly, Rotated, MinClasses, MaxConsecutive, EmptyPassword, SamePassword, CracklibCheck, Rng, GenerationFailed, UserCheck, GecosCheck, MaxClassRepeat, BadWords, MaxSequence,
UnknownError(i32),
}
impl Error {
fn from_int(i: c_int) -> Self {
match i {
-1 => Error::FatalFailure,
-2 => Error::Integer,
-3 => Error::CfgfileOpen,
-4 => Error::CfgfileMalformed,
-5 => Error::UnknownSetting,
-6 => Error::NonIntSetting,
-7 => Error::NonStrSetting,
-8 => Error::MemAlloc,
-9 => Error::TooSimilar,
-10 => Error::MinDigits,
-11 => Error::MinUppers,
-12 => Error::MinLowers,
-13 => Error::MinOthers,
-14 => Error::MinLength,
-15 => Error::Palindrome,
-16 => Error::CaseChangesOnly,
-17 => Error::Rotated,
-18 => Error::MinClasses,
-19 => Error::MaxConsecutive,
-20 => Error::EmptyPassword,
-21 => Error::SamePassword,
-22 => Error::CracklibCheck,
-23 => Error::Rng,
-24 => Error::GenerationFailed,
-25 => Error::UserCheck,
-26 => Error::GecosCheck,
-27 => Error::MaxClassRepeat,
-28 => Error::BadWords,
-29 => Error::MaxSequence,
_ => Error::UnknownError(i)
}
}
}
enum OpaqueSettings{}
#[link(name = "pwquality")]
extern {
fn pwquality_default_settings() -> *const OpaqueSettings;
fn pwquality_free_settings(pwq: *const OpaqueSettings);
fn pwquality_read_config(pwq: *const OpaqueSettings, cfgfile: *const c_char, auxerror: *mut *mut c_void) -> c_int;
fn pwquality_set_int_value(pwq: *const OpaqueSettings, setting: c_int, value: c_int) -> c_int;
fn pwquality_set_str_value(pwq: *const OpaqueSettings, setting: c_int, value: *const c_char) -> c_int;
fn pwquality_get_int_value(pwq: *const OpaqueSettings, setting: c_int, value: *mut c_int) -> c_int;
fn pwquality_get_str_value(pwq: *const OpaqueSettings, setting: c_int, value: *mut *mut c_char) -> c_int;
fn pwquality_generate(pwq: *const OpaqueSettings, entropy_bits: c_int, password: *mut *mut c_char) -> c_int;
fn pwquality_check(pwq: *const OpaqueSettings, password: *const c_char, oldpassword: *const c_char, user: *const c_char, auxerror: *mut *mut c_void) -> c_int;
}
#[derive(Debug)]
pub struct PWQuality {
pwq: *const OpaqueSettings
}
impl PWQuality {
pub fn new() -> Self {
let pwq = unsafe {
pwquality_default_settings()
};
PWQuality {
pwq
}
}
#[inline]
pub fn from_default_config() -> Result<Self, Error> {
PWQuality::from_optional_config(None::<&str>)
}
#[inline]
pub fn from_config<P: AsRef<Path>>(config_path: P) -> Result<Self, Error> {
PWQuality::from_optional_config(Some(config_path))
}
pub fn from_optional_config<P: AsRef<Path>>(config_path: Option<P>) -> Result<Self, Error> {
let c_path = match config_path {
Some(config_path) => CString::new(config_path.as_ref().to_str().unwrap()).unwrap().as_ptr(),
None => null()
};
let (res, pwq) = unsafe {
let pwq = pwquality_default_settings();
let res = pwquality_read_config(pwq, c_path, null_mut());
(res, pwq)
};
if res < 0 {
Err(Error::from_int(res))
} else {
Ok(PWQuality {
pwq
})
}
}
pub fn set_min_diff(&self, min: Option<i32>) {
let val = min.unwrap_or_else(|| { 0 });
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_DIFF_OK, val);
assert!(res == 0);
}
}
pub fn get_min_diff(&self) -> Option<i32> {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_DIFF_OK, result);
assert!(res == 0);
if *result == 0 {
None
} else {
Some(*result)
}
}
}
pub fn set_min_length(&self, min: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_MIN_LENGTH, min);
assert!(res == 0);
}
}
pub fn get_min_length(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_MIN_LENGTH, result);
assert!(res == 0);
*result
}
}
pub fn set_digit_credit(&self, credit: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_DIG_CREDIT, credit);
assert!(res == 0);
}
}
pub fn get_digit_credit(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_DIG_CREDIT, result);
assert!(res == 0);
*result
}
}
pub fn set_uppercase_credit(&self, credit: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_UP_CREDIT, credit);
assert!(res == 0);
}
}
pub fn get_uppercase_credit(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_UP_CREDIT, result);
assert!(res == 0);
*result
}
}
pub fn set_lowercase_credit(&self, credit: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_LOW_CREDIT, credit);
assert!(res == 0);
}
}
pub fn get_lowercase_credit(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_LOW_CREDIT, result);
assert!(res == 0);
*result
}
}
pub fn set_other_credit(&self, credit: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_OTH_CREDIT, credit);
assert!(res == 0);
}
}
pub fn get_other_credit(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_OTH_CREDIT, result);
assert!(res == 0);
*result
}
}
pub fn set_min_classes(&self, min: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_MIN_CLASS, min);
assert!(res == 0);
}
}
pub fn get_min_classes(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_MIN_CLASS, result);
assert!(res == 0);
*result
}
}
pub fn set_max_repeat(&self, max: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_MAX_REPEAT, max);
assert!(res == 0);
}
}
pub fn get_max_repeat(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_MAX_REPEAT, result);
assert!(res == 0);
*result
}
}
pub fn set_max_class_repeat(&self, max: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_MAX_CLASS_REPEAT, max);
assert!(res == 0);
}
}
pub fn get_max_class_repeat(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_MAX_CLASS_REPEAT, result);
assert!(res == 0);
*result
}
}
pub fn set_max_sequence(&self, max: i32) {
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_MAX_SEQUENCE, max);
assert!(res == 0);
}
}
pub fn get_max_sequence(&self) -> i32 {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_MAX_SEQUENCE, result);
assert!(res == 0);
*result
}
}
pub fn set_gecos_check(&self, check: bool) {
let value = if check { 1 } else { 0 };
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_GECOS_CHECK, value);
assert!(res == 0);
}
}
pub fn get_gecos_check(&self) -> bool {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_GECOS_CHECK, result);
assert!(res == 0);
*result != 0
}
}
pub fn set_dictionary_check(&self, check: bool) {
let value = if check { 1 } else { 0 };
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_DICT_CHECK, value);
assert!(res == 0);
}
}
pub fn get_dictionary_check(&self) -> bool {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_DICT_CHECK, result);
assert!(res == 0);
*result != 0
}
}
pub fn set_user_check(&self, check: bool) {
let value = if check { 1 } else { 0 };
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_USER_CHECK, value);
assert!(res == 0);
}
}
pub fn get_user_check(&self) -> bool {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_USER_CHECK, result);
assert!(res == 0);
*result != 0
}
}
pub fn set_enforcing(&self, check: bool) {
let value = if check { 1 } else { 0 };
unsafe {
let res = pwquality_set_int_value(self.pwq, PWQ_SETTING_ENFORCING, value);
assert!(res == 0);
}
}
pub fn get_enforcing(&self) -> bool {
unsafe {
let result: *mut i32 = &mut 0;
let res = pwquality_get_int_value(self.pwq, PWQ_SETTING_ENFORCING, result);
assert!(res == 0);
*result != 0
}
}
pub fn set_dictionary_path(&self, path: String) -> Result<(), Error>{
let c_path = CString::new(path.as_str()).unwrap();
unsafe {
let res = pwquality_set_str_value(self.pwq, PWQ_SETTING_DICT_PATH, c_path.as_ptr());
if res == 0 {
Ok(())
} else {
Err(Error::from_int(res))
}
}
}
pub fn get_dictionary_path(&self) -> Result<Option<String>, Error> {
let (res, ptr) =
unsafe {
let mut result = 0 as *mut c_char;
let res = pwquality_get_str_value(self.pwq, PWQ_SETTING_DICT_PATH, (&mut result) as *mut _ as *mut *mut c_char);
(res, result)
};
if res < 0 {
Err(Error::from_int(res))
} else if ptr == (0 as *mut c_char) {
Ok(None)
} else {
let str = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() };
Ok(Some(str))
}
}
pub fn set_bad_words(&self, bad_words: Vec<String>) -> Result<(), Error> {
let joined_str = bad_words.join(" ");
let c_str = CString::new(joined_str.as_str()).unwrap();
unsafe {
let res = pwquality_set_str_value(self.pwq, PWQ_SETTING_BAD_WORDS, c_str.as_ptr());
if res == 0 {
Ok(())
} else {
Err(Error::from_int(res))
}
}
}
pub fn get_bad_words(&self) -> Result<Vec<String>, Error> {
let (res, ptr) =
unsafe {
let mut result = 0 as *mut c_char;
let res = pwquality_get_str_value(self.pwq, PWQ_SETTING_BAD_WORDS, (&mut result) as *mut _ as *mut *mut c_char);
(res, result)
};
if res < 0 {
Err(Error::from_int(res))
} else if ptr == (0 as *mut c_char) {
Ok(vec![])
} else {
let str = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() };
let result = str.split_whitespace().map(String::from).collect();
Ok(result)
}
}
pub fn generate_password(&self, entropy: i32) -> Result<String, Error> {
let (res, ptr) =
unsafe {
let mut result = 0 as *mut c_char;
let res = pwquality_generate(self.pwq, entropy, (&mut result) as *mut _ as *mut *mut c_char);
(res, result)
};
if res < 0 {
Err(Error::from_int(res))
} else if ptr == (0 as *mut c_char) {
Ok("".to_owned())
} else {
let pwd = unsafe { CStr::from_ptr(ptr).to_string_lossy().into_owned() };
Ok(pwd)
}
}
pub fn check(&self, password: String, old_password: Option<String>, username: Option<String>) -> Result<i32, Error> {
unsafe {
let c_password = CString::new(password).unwrap();
let res =
match (old_password, username) {
(Some(old_password), Some(username)) => {
let c_old_password = CString::new(old_password).unwrap();
let c_user = CString::new(username).unwrap();
pwquality_check(self.pwq, c_password.as_ptr(), c_old_password.as_ptr(), c_user.as_ptr(), null_mut())
},
(Some(old_password), None) => {
let c_old_password = CString::new(old_password).unwrap();
pwquality_check(self.pwq, c_password.as_ptr(), c_old_password.as_ptr(), null(), null_mut())
},
(None, Some(username)) => {
let c_user = CString::new(username).unwrap();
pwquality_check(self.pwq, c_password.as_ptr(), null(), c_user.as_ptr(), null_mut())
},
(None, None) =>
pwquality_check(self.pwq, c_password.as_ptr(), null(), null(), null_mut())
};
if res < 0 {
Err(Error::from_int(res))
} else {
Ok(res)
}
}
}
}
impl Drop for PWQuality {
fn drop(&mut self) {
unsafe {
pwquality_free_settings(self.pwq);
}
}
}