comparison src/constants.rs @ 59:3f4a77aa88be

Fix string copyting and improve error situation. This change is too big and includes several things: - Fix copying strings from PAM by fixing const and mut on pam funcs. - Improve error enums by simplifying conversions and removing unnecessary and ambiguous "success" variants. - Make a bunch of casts nicer. - Assorted other cleanup.
author Paul Fisher <paul@pfish.zone>
date Wed, 21 May 2025 00:27:18 -0400
parents daa2cde64601
children 05cc2c27334f
comparison
equal deleted inserted replaced
58:868a278a362c 59:3f4a77aa88be
1 use bitflags::bitflags; 1 use bitflags::bitflags;
2 use libc::{c_int, c_uint}; 2 use libc::{c_int, c_uint};
3 use num_derive::{FromPrimitive, ToPrimitive}; 3 use num_derive::FromPrimitive;
4 use num_traits::{FromPrimitive, ToPrimitive}; 4 use num_traits::FromPrimitive;
5 use std::any;
6 use std::marker::PhantomData;
5 // TODO: Import constants from C header file at compile time. 7 // TODO: Import constants from C header file at compile time.
6 8
7 // The Linux-PAM flags 9 // The Linux-PAM flags
8 // see /usr/include/security/_pam_types.h 10 // see /usr/include/security/_pam_types.h
9 bitflags! { 11 bitflags! {
19 const CHANGE_EXPIRED_AUTHTOK= 0x0020; 21 const CHANGE_EXPIRED_AUTHTOK= 0x0020;
20 } 22 }
21 } 23 }
22 24
23 /// Styles of message that are shown to the user. 25 /// Styles of message that are shown to the user.
24 #[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)] 26 #[derive(Debug, PartialEq, FromPrimitive)]
25 #[non_exhaustive] // non-exhaustive because C might give us back anything! 27 #[non_exhaustive] // non-exhaustive because C might give us back anything!
26 pub enum MessageStyle { 28 pub enum MessageStyle {
27 /// Requests information from the user; will be masked when typing. 29 /// Requests information from the user; will be masked when typing.
28 PromptEchoOff = 1, 30 PromptEchoOff = 1,
29 /// Requests information from the user; will not be masked. 31 /// Requests information from the user; will not be masked.
37 /// For server–client non-human interaction. 39 /// For server–client non-human interaction.
38 /// NOT part of the X/Open PAM specification. 40 /// NOT part of the X/Open PAM specification.
39 BinaryPrompt = 7, 41 BinaryPrompt = 7,
40 } 42 }
41 43
44 impl TryFrom<c_int> for MessageStyle {
45 type Error = InvalidEnum<Self>;
46 fn try_from(value: c_int) -> Result<Self, Self::Error> {
47 Self::from_i32(value).ok_or(value.into())
48 }
49 }
50
42 impl From<MessageStyle> for c_int { 51 impl From<MessageStyle> for c_int {
43 fn from(val: MessageStyle) -> Self { 52 fn from(val: MessageStyle) -> Self {
44 val.to_i32().unwrap_or(0) 53 val as Self
45 } 54 }
46 } 55 }
47 56
48 /// The Linux-PAM error return values. 57 /// The Linux-PAM error return values.
49 /// Success is instead represented by the `Ok` entry of a `Result`. 58 /// Success is instead represented by the `Ok` entry of a `Result`.
50 /// Most abbreviations (except `AuthTok` and `Max`) are now full words. 59 /// Most abbreviations (except `AuthTok` and `Max`) are now full words.
51 /// For more detailed information, see 60 /// For more detailed information, see
52 /// `/usr/include/security/_pam_types.h`. 61 /// `/usr/include/security/_pam_types.h`.
53 #[allow(non_camel_case_types, dead_code)] 62 #[allow(non_camel_case_types, dead_code)]
54 #[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive, ToPrimitive)] 63 #[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive)]
55 #[non_exhaustive] // C might give us anything! 64 #[non_exhaustive] // C might give us anything!
56 pub enum ErrorCode { 65 pub enum ErrorCode {
57 #[error("dlopen() failure when dynamically loading a service module")] 66 #[error("dlopen() failure when dynamically loading a service module")]
58 OpenError = 1, 67 OpenError = 1,
59 #[error("symbol not found")] 68 #[error("symbol not found")]
127 Err(otherwise) => otherwise.into(), 136 Err(otherwise) => otherwise.into(),
128 } 137 }
129 } 138 }
130 139
131 /// Converts a C result code into a PamResult, with success as Ok. 140 /// Converts a C result code into a PamResult, with success as Ok.
141 /// Invalid values are returned as a [Self::SystemError].
132 pub fn result_from(value: c_int) -> PamResult<()> { 142 pub fn result_from(value: c_int) -> PamResult<()> {
133 match value { 143 match value {
134 0 => Ok(()), 144 0 => Ok(()),
135 value => Err(Self::from_i64(value as i64).unwrap_or(Self::ConversationError)) 145 value => Err(value.try_into().unwrap_or(Self::SystemError)),
136 } 146 }
147 }
148 }
149
150 impl TryFrom<c_int> for ErrorCode {
151 type Error = InvalidEnum<Self>;
152
153 fn try_from(value: c_int) -> Result<Self, Self::Error> {
154 Self::from_i32(value).ok_or(value.into())
137 } 155 }
138 } 156 }
139 157
140 impl From<ErrorCode> for c_int { 158 impl From<ErrorCode> for c_int {
141 fn from(val: ErrorCode) -> Self { 159 fn from(val: ErrorCode) -> Self {
142 val.to_i32().unwrap_or(0) 160 val as Self
143 } 161 }
144 } 162 }
163
164 /// Error returned when attempting to coerce an invalid C integer into an enum.
165 #[derive(thiserror::Error)]
166 #[error("{} is not a valid {}", .0, any::type_name::<T>())]
167 #[derive(Debug, PartialEq)]
168 pub struct InvalidEnum<T>(std::ffi::c_int, PhantomData<T>);
169
170 impl<T> From<std::ffi::c_int> for InvalidEnum<T> {
171 fn from(value: std::ffi::c_int) -> Self {
172 Self(value, PhantomData)
173 }
174 }
175
176 #[cfg(test)]
177 mod tests {
178 use super::*;
179
180 #[test]
181 fn test_enums() {
182 assert_eq!(Ok(MessageStyle::ErrorMsg), 3.try_into());
183 assert_eq!(Err(999.into()), ErrorCode::try_from(999));
184 assert_eq!(Ok(()), ErrorCode::result_from(0));
185 assert_eq!(Err(ErrorCode::Abort), ErrorCode::result_from(26));
186 assert_eq!(Err(ErrorCode::SystemError), ErrorCode::result_from(423));
187 }
188 }