comparison src/constants.rs @ 56:daa2cde64601

Big big refactor. Probably should have been multiple changes. - Makes FFI safer by explicitly specifying c_int in calls. - Uses ToPrimitive/FromPrimitive to make this easier. - Pulls PamFlag variables into a bitflags! struct. - Pulls PamMessageStyle variables into an enum. - Renames ResultCode to ErrorCode. - Switches from PAM_SUCCESS to using a Result<(), ErrorCode>. - Uses thiserror to make ErrorCode into an Error. - Gets rid of pam_try! because now we have Results. - Expands some names (e.g. Conv to Conversation). - Adds more doc comments. - Returns passwords as a SecureString, to avoid unnecessarily keeping it around in memory.
author Paul Fisher <paul@pfish.zone>
date Sun, 04 May 2025 02:56:55 -0400
parents 676675c3d434
children 3f4a77aa88be
comparison
equal deleted inserted replaced
55:676675c3d434 56:daa2cde64601
1 use bitflags::bitflags;
1 use libc::{c_int, c_uint}; 2 use libc::{c_int, c_uint};
2 3 use num_derive::{FromPrimitive, ToPrimitive};
4 use num_traits::{FromPrimitive, ToPrimitive};
3 // TODO: Import constants from C header file at compile time. 5 // TODO: Import constants from C header file at compile time.
4
5 pub type PamFlag = c_uint;
6 pub type PamItemType = c_int;
7 pub type PamMessageStyle = c_int;
8 6
9 // The Linux-PAM flags 7 // The Linux-PAM flags
10 // see /usr/include/security/_pam_types.h 8 // see /usr/include/security/_pam_types.h
11 pub const PAM_SILENT: PamFlag = 0x8000; 9 bitflags! {
12 pub const PAM_DISALLOW_NULL_AUTHTOK: PamFlag = 0x0001; 10 #[derive(Debug, PartialEq)]
13 pub const PAM_ESTABLISH_CRED: PamFlag = 0x0002; 11 #[repr(transparent)]
14 pub const PAM_DELETE_CRED: PamFlag = 0x0004; 12 pub struct Flags: c_uint {
15 pub const PAM_REINITIALIZE_CRED: PamFlag = 0x0008; 13 const SILENT = 0x8000;
16 pub const PAM_REFRESH_CRED: PamFlag = 0x0010; 14 const DISALLOW_NULL_AUTHTOK = 0x0001;
17 pub const PAM_CHANGE_EXPIRED_AUTHTOK: PamFlag = 0x0020; 15 const ESTABLISH_CRED = 0x0002;
16 const DELETE_CRED = 0x0004;
17 const REINITIALIZE_CRED = 0x0008;
18 const REFRESH_CRED = 0x0010;
19 const CHANGE_EXPIRED_AUTHTOK= 0x0020;
20 }
21 }
18 22
19 // Message styles 23 /// Styles of message that are shown to the user.
20 pub const PAM_PROMPT_ECHO_OFF: PamMessageStyle = 1; 24 #[derive(Debug, PartialEq, FromPrimitive, ToPrimitive)]
21 pub const PAM_PROMPT_ECHO_ON: PamMessageStyle = 2; 25 #[non_exhaustive] // non-exhaustive because C might give us back anything!
22 pub const PAM_ERROR_MSG: PamMessageStyle = 3; 26 pub enum MessageStyle {
23 pub const PAM_TEXT_INFO: PamMessageStyle = 4; 27 /// Requests information from the user; will be masked when typing.
24 /// yes/no/maybe conditionals 28 PromptEchoOff = 1,
25 pub const PAM_RADIO_TYPE: PamMessageStyle = 5; 29 /// Requests information from the user; will not be masked.
26 pub const PAM_BINARY_PROMPT: PamMessageStyle = 7; 30 PromptEchoOn = 2,
31 /// An error message.
32 ErrorMsg = 3,
33 /// An informational message.
34 TextInfo = 4,
35 /// Yes/No/Maybe conditionals. Linux-PAM specific.
36 RadioType = 5,
37 /// For server–client non-human interaction.
38 /// NOT part of the X/Open PAM specification.
39 BinaryPrompt = 7,
40 }
27 41
28 /// The Linux-PAM return values. 42 impl From<MessageStyle> for c_int {
43 fn from(val: MessageStyle) -> Self {
44 val.to_i32().unwrap_or(0)
45 }
46 }
47
48 /// The Linux-PAM error return values.
49 /// Success is instead represented by the `Ok` entry of a `Result`.
50 /// Most abbreviations (except `AuthTok` and `Max`) are now full words.
29 /// For more detailed information, see 51 /// For more detailed information, see
30 /// /usr/include/security/_pam_types.h 52 /// `/usr/include/security/_pam_types.h`.
31 #[allow(non_camel_case_types, dead_code)] 53 #[allow(non_camel_case_types, dead_code)]
32 #[derive(Debug, PartialEq, thiserror::Error)] 54 #[derive(Copy, Clone, Debug, PartialEq, thiserror::Error, FromPrimitive, ToPrimitive)]
33 #[repr(C)] 55 #[non_exhaustive] // C might give us anything!
34 pub enum PamResultCode { 56 pub enum ErrorCode {
35 #[error("Not an error")]
36 PAM_SUCCESS = 0,
37 #[error("dlopen() failure when dynamically loading a service module")] 57 #[error("dlopen() failure when dynamically loading a service module")]
38 PAM_OPEN_ERR = 1, 58 OpenError = 1,
39 #[error("symbol not found")] 59 #[error("symbol not found")]
40 PAM_SYMBOL_ERR = 2, 60 SymbolError = 2,
41 #[error("error in service module")] 61 #[error("error in service module")]
42 PAM_SERVICE_ERR = 3, 62 ServiceError = 3,
43 #[error("system error")] 63 #[error("system error")]
44 PAM_SYSTEM_ERR = 4, 64 SystemError = 4,
45 #[error("memory buffer error")] 65 #[error("memory buffer error")]
46 PAM_BUF_ERR = 5, 66 BufferError = 5,
47 #[error("permission denied")] 67 #[error("permission denied")]
48 PAM_PERM_DENIED = 6, 68 PermissionDenied = 6,
49 #[error("authentication failure")] 69 #[error("authentication failure")]
50 PAM_AUTH_ERR = 7, 70 AuthenticationError = 7,
51 #[error("cannot access authentication data due to insufficient credentials")] 71 #[error("cannot access authentication data due to insufficient credentials")]
52 PAM_CRED_INSUFFICIENT = 8, 72 CredentialsInsufficient = 8,
53 #[error("underlying authentication service cannot retrieve authentication information")] 73 #[error("underlying authentication service cannot retrieve authentication information")]
54 PAM_AUTHINFO_UNAVAIL = 9, 74 AuthInfoUnavailable = 9,
55 #[error("user not known to the underlying authentication module")] 75 #[error("user not known to the underlying authentication module")]
56 PAM_USER_UNKNOWN = 10, 76 UserUnknown = 10,
57 #[error("retry limit reached; do not attempt further")] 77 #[error("retry limit reached; do not attempt further")]
58 PAM_MAXTRIES = 11, 78 MaxTries = 11,
59 #[error("new authentication token required")] 79 #[error("new authentication token required")]
60 PAM_NEW_AUTHTOK_REQD = 12, 80 NewAuthTokRequired = 12,
61 #[error("user account has expired")] 81 #[error("user account has expired")]
62 PAM_ACCT_EXPIRED = 13, 82 AccountExpired = 13,
63 #[error("cannot make/remove an entry for the specified session")] 83 #[error("cannot make/remove an entry for the specified session")]
64 PAM_SESSION_ERR = 14, 84 SessionError = 14,
65 #[error("underlying authentication service cannot retrieve user credentials")] 85 #[error("underlying authentication service cannot retrieve user credentials")]
66 PAM_CRED_UNAVAIL = 15, 86 CredentialsUnavailable = 15,
67 #[error("user credentials expired")] 87 #[error("user credentials expired")]
68 PAM_CRED_EXPIRED = 16, 88 CredentialsExpired = 16,
69 #[error("failure setting user credentials")] 89 #[error("failure setting user credentials")]
70 PAM_CRED_ERR = 17, 90 CredentialsError = 17,
71 #[error("no module-specific data is present")] 91 #[error("no module-specific data is present")]
72 PAM_NO_MODULE_DATA = 18, 92 NoModuleData = 18,
73 #[error("conversation error")] 93 #[error("conversation error")]
74 PAM_CONV_ERR = 19, 94 ConversationError = 19,
75 #[error("authentication token manipulation error")] 95 #[error("authentication token manipulation error")]
76 PAM_AUTHTOK_ERR = 20, 96 AuthTokError = 20,
77 #[error("authentication information cannot be recovered")] 97 #[error("authentication information cannot be recovered")]
78 PAM_AUTHTOK_RECOVERY_ERR = 21, 98 AuthTokRecoveryError = 21,
79 #[error("authentication token lock busy")] 99 #[error("authentication token lock busy")]
80 PAM_AUTHTOK_LOCK_BUSY = 22, 100 AuthTokLockBusy = 22,
81 #[error("authentication token aging disabled")] 101 #[error("authentication token aging disabled")]
82 PAM_AUTHTOK_DISABLE_AGING = 23, 102 AuthTokDisableAging = 23,
83 #[error("preliminary check by password service")] 103 #[error("preliminary check by password service")]
84 PAM_TRY_AGAIN = 24, 104 TryAgain = 24,
85 #[error("ignore underlying account module, regardless of control flag")] 105 #[error("ignore underlying account module, regardless of control flag")]
86 PAM_IGNORE = 25, 106 Ignore = 25,
87 #[error("critical error; this module should fail now")] 107 #[error("critical error; this module should fail now")]
88 PAM_ABORT = 26, 108 Abort = 26,
89 #[error("authentication token has expired")] 109 #[error("authentication token has expired")]
90 PAM_AUTHTOK_EXPIRED = 27, 110 AuthTokExpired = 27,
91 #[error("module is not known")] 111 #[error("module is not known")]
92 PAM_MODULE_UNKNOWN = 28, 112 ModuleUnknown = 28,
93 #[error("bad item passed to pam_[whatever]_item")] 113 #[error("bad item passed to pam_[whatever]_item")]
94 PAM_BAD_ITEM = 29, 114 BadItem = 29,
95 #[error("conversation function is event-driven and data is not available yet")] 115 #[error("conversation function is event-driven and data is not available yet")]
96 PAM_CONV_AGAIN = 30, 116 ConversationAgain = 30,
97 #[error("call this function again to complete authentication stack")] 117 #[error("call this function again to complete authentication stack")]
98 PAM_INCOMPLETE = 31, 118 Incomplete = 31,
99 } 119 }
120
121 pub type PamResult<T> = Result<T, ErrorCode>;
122 impl ErrorCode {
123 /// Converts a PamResult into the result code that C wants.
124 pub fn result_to_c(value: PamResult<()>) -> c_int {
125 match value {
126 Ok(_) => 0, // PAM_SUCCESS
127 Err(otherwise) => otherwise.into(),
128 }
129 }
130
131 /// Converts a C result code into a PamResult, with success as Ok.
132 pub fn result_from(value: c_int) -> PamResult<()> {
133 match value {
134 0 => Ok(()),
135 value => Err(Self::from_i64(value as i64).unwrap_or(Self::ConversationError))
136 }
137 }
138 }
139
140 impl From<ErrorCode> for c_int {
141 fn from(val: ErrorCode) -> Self {
142 val.to_i32().unwrap_or(0)
143 }
144 }