Mercurial > crates > nonstick
comparison src/handle.rs @ 80:5aa1a010f1e8
Start using PAM headers; improve owned/borrowed distinction.
- Uses bindgen to generate bindings (only if needed).
- Gets the story together on owned vs. borrowed handles.
- Reduces number of mutable borrows in handle operation
(since `PamHandle` is neither `Send` nor `Sync`,
we never have to worry about thread safety.
- Improves a bunch of macros so we don't have our own
special syntax for docs.
- Implement question indirection for standard XSSO PAM implementations.
| author | Paul Fisher <paul@pfish.zone> |
|---|---|
| date | Tue, 10 Jun 2025 01:09:30 -0400 |
| parents | 002adfb98c5c |
| children | f6186e41399b |
comparison
equal
deleted
inserted
replaced
| 79:2128123b9406 | 80:5aa1a010f1e8 |
|---|---|
| 1 //! The wrapper types and traits for handles into the PAM library. | 1 //! The wrapper types and traits for handles into the PAM library. |
| 2 | |
| 2 use crate::constants::Result; | 3 use crate::constants::Result; |
| 3 use crate::conv::Conversation; | 4 use crate::conv::Conversation; |
| 4 | 5 |
| 5 macro_rules! trait_item { | 6 macro_rules! trait_item { |
| 6 (get = $getter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { | 7 ($(#[$md:meta])* get = $getter:ident, item = $item:literal $(, see = $see:path)?) => { |
| 7 $( | 8 $(#[$md])* |
| 8 $(#[doc = $doc])* | 9 #[doc = ""] |
| 9 #[doc = ""] | |
| 10 )? | |
| 11 #[doc = concat!("Gets the `", $item, "` of the PAM handle.")] | 10 #[doc = concat!("Gets the `", $item, "` of the PAM handle.")] |
| 12 $( | 11 $( |
| 13 #[doc = concat!("See [`", stringify!($see), "`].")] | 12 #[doc = concat!("See [`", stringify!($see), "`].")] |
| 14 )? | 13 )? |
| 15 #[doc = ""] | 14 /// |
| 16 #[doc = "Returns a reference to the item's value, owned by PAM."] | 15 /// Returns a reference to the item's value, owned by PAM. |
| 17 #[doc = "The item is assumed to be valid UTF-8 text."] | 16 /// The item is assumed to be valid UTF-8 text. |
| 18 #[doc = "If it is not, `ConversationError` is returned."] | 17 /// If it is not, `ConversationError` is returned. |
| 19 #[doc = ""] | 18 /// |
| 20 #[doc = "See the [`pam_get_item`][man] manual page,"] | 19 /// See the [`pam_get_item`][man] manual page, |
| 21 #[doc = "[`pam_get_item` in the Module Writers' Guide][mwg], or"] | 20 /// [`pam_get_item` in the Module Writers' Guide][mwg], or |
| 22 #[doc = "[`pam_get_item` in the Application Developers' Guide][adg]."] | 21 /// [`pam_get_item` in the Application Developers' Guide][adg]. |
| 23 #[doc = ""] | 22 /// |
| 24 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html"] | 23 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_item.3.html |
| 25 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item"] | 24 /// [adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_get_item |
| 26 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item"] | 25 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item |
| 27 fn $getter(&mut self) -> Result<Option<&str>>; | 26 fn $getter(&self) -> Result<Option<&str>>; |
| 28 }; | 27 }; |
| 29 (set = $setter:ident, item = $item:literal $(, see = $see:path)? $(, $($doc:literal)*)?) => { | 28 ($(#[$md:meta])* set = $setter:ident, item = $item:literal $(, see = $see:path)?) => { |
| 30 $( | 29 $(#[$md])* |
| 31 $(#[doc = $doc])* | |
| 32 #[doc = ""] | |
| 33 )? | |
| 34 #[doc = concat!("Sets the `", $item, "` from the PAM handle.")] | 30 #[doc = concat!("Sets the `", $item, "` from the PAM handle.")] |
| 35 $( | 31 $( |
| 36 #[doc = concat!("See [`", stringify!($see), "`].")] | 32 #[doc = concat!("See [`", stringify!($see), "`].")] |
| 37 )? | 33 )? |
| 38 #[doc = ""] | 34 /// |
| 39 #[doc = "Sets the item's value. PAM copies the string's contents."] | 35 /// Sets the item's value. PAM copies the string's contents. |
| 40 #[doc = "If the string contains a null byte, this will return "] | 36 /// If the string contains a null byte, this will return |
| 41 #[doc = "a `ConversationError`."] | 37 /// a `ConversationError`. |
| 42 #[doc = ""] | 38 /// |
| 43 #[doc = "See the [`pam_set_item`][man] manual page,"] | 39 /// See the [`pam_set_item`][man] manual page, |
| 44 #[doc = "[`pam_set_item` in the Module Writers' Guide][mwg], or"] | 40 /// [`pam_set_item` in the Module Writers' Guide][mwg], or |
| 45 #[doc = "[`pam_set_item` in the Application Developers' Guide][adg]."] | 41 /// [`pam_set_item` in the Application Developers' Guide][adg]. |
| 46 #[doc = ""] | 42 /// |
| 47 #[doc = "[man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html"] | 43 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_set_item.3.html |
| 48 #[doc = "[adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item"] | 44 /// [adg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/adg-interface-by-app-expected.html#adg-pam_set_item |
| 49 #[doc = "[mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item"] | 45 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_set_item |
| 50 fn $setter(&mut self, value: Option<&str>) -> Result<()>; | 46 fn $setter(&mut self, value: Option<&str>) -> Result<()>; |
| 51 }; | 47 }; |
| 52 } | 48 } |
| 53 | |
| 54 /// All-in-one trait for what you should expect from PAM as an application. | |
| 55 pub trait PamHandleApplication: PamApplicationOnly + PamShared {} | |
| 56 impl<T> PamHandleApplication for T where T: PamApplicationOnly + PamShared {} | |
| 57 | |
| 58 /// All-in-one trait for what you should expect from PAM as a module. | |
| 59 pub trait PamHandleModule: PamModuleOnly + PamShared {} | |
| 60 impl<T> PamHandleModule for T where T: PamModuleOnly + PamShared {} | |
| 61 | 49 |
| 62 /// Functionality for both PAM applications and PAM modules. | 50 /// Functionality for both PAM applications and PAM modules. |
| 63 /// | 51 /// |
| 64 /// This base trait includes features of a PAM handle that are available | 52 /// This base trait includes features of a PAM handle that are available |
| 65 /// to both applications and modules. | 53 /// to both applications and modules. |
| 98 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html | 86 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_user.3.html |
| 99 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user | 87 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_user |
| 100 fn get_user(&mut self, prompt: Option<&str>) -> Result<&str>; | 88 fn get_user(&mut self, prompt: Option<&str>) -> Result<&str>; |
| 101 | 89 |
| 102 trait_item!( | 90 trait_item!( |
| 91 /// The identity of the user for whom service is being requested. | |
| 92 /// | |
| 93 /// Unlike [`get_user`](Self::get_user), this will simply get | |
| 94 /// the current state of the user item, and not request the username. | |
| 95 /// While PAM usually sets this automatically in the `get_user` call, | |
| 96 /// it may be changed by a module during the PAM transaction. | |
| 97 /// Applications should check it after each step of the PAM process. | |
| 103 get = user_item, | 98 get = user_item, |
| 104 item = "PAM_USER", | 99 item = "PAM_USER", |
| 105 see = Self::get_user, | 100 see = Self::get_user |
| 106 "The identity of the user for whom service is being requested." | 101 ); |
| 107 "" | 102 trait_item!( |
| 108 "Unlike [`get_user`](Self::get_user), this will simply get" | 103 /// Sets the identity of the logging-in user. |
| 109 "the current state of the user item, and not request the username. " | 104 /// |
| 110 "While PAM usually sets this automatically in the `get_user` call, " | 105 /// Usually this will be set during the course of |
| 111 "it may be changed by a module during the PAM transaction. " | 106 /// a [`get_user`](Self::get_user) call, but you may set it manually |
| 112 "Applications should check it after each step of the PAM process." | 107 /// or change it during the PAM process. |
| 113 ); | |
| 114 trait_item!( | |
| 115 set = set_user_item, | 108 set = set_user_item, |
| 116 item = "PAM_USER", | 109 item = "PAM_USER", |
| 117 see = Self::user_item, | 110 see = Self::user_item |
| 118 "Sets the identity of the logging-in user." | 111 ); |
| 119 "" | 112 |
| 120 "Usually this will be set during the course of " | 113 trait_item!( |
| 121 "a [`get_user`](Self::get_user) call, but you may set it manually " | 114 /// The service name, which identifies the PAM stack which is used |
| 122 "or change it during the PAM process." | 115 /// to perform authentication. |
| 123 ); | |
| 124 | |
| 125 trait_item!( | |
| 126 get = service, | 116 get = service, |
| 127 item = "PAM_SERVICE", | 117 item = "PAM_SERVICE" |
| 128 "The service name, which identifies the PAM stack which is used " | 118 ); |
| 129 "to perform authentication." | 119 trait_item!( |
| 130 ); | 120 /// The service name, which identifies the PAM stack which is used |
| 131 trait_item!( | 121 /// to perform authentication. It's probably a bad idea to change this. |
| 132 set = set_service, | 122 set = set_service, |
| 133 item = "PAM_SERVICE", | 123 item = "PAM_SERVICE", |
| 134 see = Self::service, | 124 see = Self::service |
| 135 "The service name, which identifies the PAM stack which is used " | 125 ); |
| 136 "to perform authentication. It's probably a bad idea to change this." | 126 |
| 137 ); | 127 trait_item!( |
| 138 | 128 /// The string used to prompt for a user's name. |
| 139 trait_item!( | 129 /// By default, this is a localized version of `login: `. |
| 140 get = user_prompt, | 130 get = user_prompt, |
| 141 item = "PAM_USER_PROMPT", | 131 item = "PAM_USER_PROMPT" |
| 142 "The string used to prompt for a user's name." | 132 ); |
| 143 "By default, this is a localized version of `login: `." | 133 trait_item!( |
| 144 ); | 134 /// Sets the string used to prompt for a user's name. |
| 145 trait_item!( | |
| 146 set = set_user_prompt, | 135 set = set_user_prompt, |
| 147 item = "PAM_USER_PROMPT", | 136 item = "PAM_USER_PROMPT", |
| 148 see = Self::user_prompt, | 137 see = Self::user_prompt |
| 149 "Sets the string used to prompt for a user's name." | 138 ); |
| 150 ); | 139 |
| 151 | 140 trait_item!( |
| 152 trait_item!( | 141 /// "The terminal name prefixed by /dev/ for device files." |
| 142 /// | |
| 143 /// This is the terminal the user is logging in on. | |
| 144 /// Very old applications may use this instead of `PAM_XDISPLAY`. | |
| 153 get = tty_name, | 145 get = tty_name, |
| 154 item = "PAM_TTY", | 146 item = "PAM_TTY" |
| 155 "\"The terminal name prefixed by /dev/ for device files.\"" | 147 ); |
| 156 "" | 148 trait_item!( |
| 157 "This is the terminal the user is logging in on." | 149 /// Sets the terminal name. |
| 158 "Very old applications may use this instead of `PAM_XDISPLAY`." | 150 /// |
| 159 ); | 151 /// (TODO: See if libpam sets this itself or if the application does.) |
| 160 trait_item!( | |
| 161 set = set_tty_name, | 152 set = set_tty_name, |
| 162 item = "PAM_TTY", | 153 item = "PAM_TTY", |
| 163 see = Self::tty_name, | 154 see = Self::tty_name |
| 164 "Sets the terminal name." | 155 ); |
| 165 "" | 156 |
| 166 "(TODO: See if libpam sets this itself or if the application does.)" | 157 trait_item!( |
| 167 ); | 158 /// If set, the identity of the remote user logging in. |
| 168 | 159 /// |
| 169 trait_item!( | 160 /// This is only as trustworthy as the application calling PAM. |
| 161 /// Also see [`remote_host`](Self::remote_host). | |
| 170 get = remote_user, | 162 get = remote_user, |
| 171 item = "PAM_RUSER", | 163 item = "PAM_RUSER" |
| 172 "If set, the identity of the remote user logging in." | 164 ); |
| 173 "" | 165 trait_item!( |
| 174 "This is only as trustworthy as the application calling PAM." | 166 /// Sets the identity of the remote user logging in. |
| 175 "Also see [`remote_host`](Self::remote_host)." | 167 /// |
| 176 ); | 168 /// This is usually set by the application before making calls |
| 177 trait_item!( | 169 /// into a PAM session. (TODO: check this!) |
| 178 set = set_remote_user, | 170 set = set_remote_user, |
| 179 item = "PAM_RUSER", | 171 item = "PAM_RUSER" |
| 180 "Sets the identity of the remote user logging in." | 172 ); |
| 181 "" | 173 |
| 182 "This is usually set by the application before making calls " | 174 trait_item!( |
| 183 "into a PAM session. (TODO: check this!)" | 175 /// If set, the remote location where the user is coming from. |
| 184 ); | 176 /// |
| 185 | 177 /// This is only as trustworthy as the application calling PAM. |
| 186 trait_item!( | 178 /// This can be combined with [`Self::remote_user`] to identify |
| 179 /// the account the user is attempting to log in from, | |
| 180 /// with `remote_user@remote_host`. | |
| 181 /// | |
| 182 /// If unset, "it is unclear where the authentication request | |
| 183 /// is originating from." | |
| 187 get = remote_host, | 184 get = remote_host, |
| 188 item = "PAM_RHOST", | 185 item = "PAM_RHOST" |
| 189 "If set, the remote location where the user is coming from." | 186 ); |
| 190 "" | 187 trait_item!( |
| 191 "This is only as trustworthy as the application calling PAM. " | 188 /// Sets the location where the user is coming from. |
| 192 "This can be combined with [`Self::remote_user`] to identify " | 189 /// |
| 193 "the account the user is attempting to log in from, " | 190 /// This is usually set by the application before making calls |
| 194 "with `remote_user@remote_host`." | 191 /// into a PAM session. (TODO: check this!) |
| 195 "" | |
| 196 "If unset, \"it is unclear where the authentication request " | |
| 197 "is originating from.\"" | |
| 198 ); | |
| 199 trait_item!( | |
| 200 set = set_remote_host, | 192 set = set_remote_host, |
| 201 item = "PAM_RHOST", | 193 item = "PAM_RHOST", |
| 202 see = Self::remote_host, | 194 see = Self::remote_host |
| 203 "Sets the location where the user is coming from." | 195 ); |
| 204 "" | 196 |
| 205 "This is usually set by the application before making calls " | 197 trait_item!( |
| 206 "into a PAM session. (TODO: check this!)" | 198 /// Gets the user's authentication token (e.g., password). |
| 207 ); | 199 /// |
| 208 | 200 /// This is usually set automatically when |
| 209 trait_item!( | 201 /// [`get_authtok`](PamHandleModule::get_authtok) is called, |
| 202 /// but can be manually set. | |
| 210 set = set_authtok_item, | 203 set = set_authtok_item, |
| 211 item = "PAM_AUTHTOK", | 204 item = "PAM_AUTHTOK", |
| 212 see = PamModuleOnly::authtok_item, | 205 see = PamHandleModule::authtok_item |
| 213 "Gets the user's authentication token (e.g., password)." | 206 ); |
| 214 "" | 207 |
| 215 "This is usually set automatically when " | 208 trait_item!( |
| 216 "[`get_authtok`](PamModuleOnly::get_authtok) is called, " | 209 /// Sets the user's "old authentication token" when changing passwords. |
| 217 "but can be manually set." | 210 // |
| 218 ); | 211 /// This is usually set automatically by PAM. |
| 219 | |
| 220 trait_item!( | |
| 221 set = set_old_authtok_item, | 212 set = set_old_authtok_item, |
| 222 item = "PAM_OLDAUTHTOK", | 213 item = "PAM_OLDAUTHTOK", |
| 223 see = PamModuleOnly::old_authtok_item, | 214 see = PamHandleModule::old_authtok_item |
| 224 "Sets the user's \"old authentication token\" when changing passwords." | |
| 225 "" | |
| 226 "This is usually set automatically by PAM." | |
| 227 ); | 215 ); |
| 228 } | 216 } |
| 229 | 217 |
| 230 /// Functionality of a PAM handle that can be expected by a PAM application. | 218 /// Functionality of a PAM handle that can be expected by a PAM application. |
| 231 /// | 219 /// |
| 232 /// If you are not writing a PAM client application (e.g., you are writing | 220 /// If you are not writing a PAM client application (e.g., you are writing |
| 233 /// a module), you should not use the functionality exposed by this trait. | 221 /// a module), you should not use the functionality exposed by this trait. |
| 234 /// | 222 /// |
| 235 /// Like [`PamShared`], this is intended to allow creating mock implementations | 223 /// Like [`PamShared`], this is intended to allow creating mock implementations |
| 236 /// of PAM for testing PAM applications. | 224 /// of PAM for testing PAM applications. |
| 237 pub trait PamApplicationOnly { | 225 pub trait PamHandleApplication: PamShared { |
| 238 /// Closes the PAM session on an owned PAM handle. | 226 // reserved! |
| 239 /// | |
| 240 /// This should be called with the result of the application's last call | |
| 241 /// into PAM services. Since this is only applicable to *owned* PAM handles, | |
| 242 /// a PAM module should never call this (and it will never be handed | |
| 243 /// an owned `PamHandle` that it can `close`). | |
| 244 /// | |
| 245 /// See the [`pam_end` manual page][man] for more information. | |
| 246 /// | |
| 247 /// ```no_run | |
| 248 /// # use nonstick::handle::PamApplicationOnly; | |
| 249 /// # use std::error::Error; | |
| 250 /// # fn _doc(handle: impl PamApplicationOnly, auth_result: nonstick::Result<()>) -> Result<(), Box<dyn Error>> { | |
| 251 /// // Earlier: authentication was performed and the result was stored | |
| 252 /// // into auth_result. | |
| 253 /// handle.close(auth_result)?; | |
| 254 /// # Ok(()) | |
| 255 /// # } | |
| 256 /// ``` | |
| 257 /// | |
| 258 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_end.3.html | |
| 259 fn close(self, status: Result<()>) -> Result<()>; | |
| 260 } | 227 } |
| 261 | 228 |
| 262 /// Functionality of a PAM handle that can be expected by a PAM module. | 229 /// Functionality of a PAM handle that can be expected by a PAM module. |
| 263 /// | 230 /// |
| 264 /// If you are not writing a PAM module (e.g., you are writing an application), | 231 /// If you are not writing a PAM module (e.g., you are writing an application), |
| 265 /// you should not use any of the functionality exposed by this trait. | 232 /// you should not use any of the functionality exposed by this trait. |
| 266 /// | 233 /// |
| 267 /// Like [`PamShared`], this is intended to allow creating mock implementations | 234 /// Like [`PamShared`], this is intended to allow creating mock implementations |
| 268 /// of PAM for testing PAM modules. | 235 /// of PAM for testing PAM modules. |
| 269 pub trait PamModuleOnly: Conversation { | 236 pub trait PamHandleModule: Conversation + PamShared { |
| 270 /// Retrieves the authentication token from the user. | 237 /// Retrieves the authentication token from the user. |
| 271 /// | 238 /// |
| 272 /// This should only be used by *authentication* and *password-change* | 239 /// This should only be used by *authentication* and *password-change* |
| 273 /// PAM modules. | 240 /// PAM modules. |
| 274 /// | 241 /// |
| 276 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. | 243 /// or [`pam_get_item` in the Module Writer's Guide][mwg]. |
| 277 /// | 244 /// |
| 278 /// # Example | 245 /// # Example |
| 279 /// | 246 /// |
| 280 /// ```no_run | 247 /// ```no_run |
| 281 /// # use nonstick::handle::PamModuleOnly; | 248 /// # use nonstick::handle::PamHandleModule; |
| 282 /// # fn _doc(handle: &mut impl PamModuleOnly) -> Result<(), Box<dyn std::error::Error>> { | 249 /// # fn _doc(handle: &mut impl PamHandleModule) -> Result<(), Box<dyn std::error::Error>> { |
| 283 /// // Get the user's password using the default prompt. | 250 /// // Get the user's password using the default prompt. |
| 284 /// let pass = handle.get_authtok(None)?; | 251 /// let pass = handle.get_authtok(None)?; |
| 285 /// // Get the user's password using a custom prompt. | 252 /// // Get the user's password using a custom prompt. |
| 286 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; | 253 /// let pass = handle.get_authtok(Some("Reveal your secrets!"))?; |
| 287 /// Ok(()) | 254 /// Ok(()) |
| 291 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html | 258 /// [man]: https://www.man7.org/linux/man-pages/man3/pam_get_authtok.3.html |
| 292 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item | 259 /// [mwg]: https://www.chiark.greenend.org.uk/doc/libpam-doc/html/mwg-expected-by-module-item.html#mwg-pam_get_item |
| 293 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str>; | 260 fn get_authtok(&mut self, prompt: Option<&str>) -> Result<&str>; |
| 294 | 261 |
| 295 trait_item!( | 262 trait_item!( |
| 263 /// Gets the user's authentication token (e.g., password). | |
| 264 /// | |
| 265 /// This is normally set automatically by PAM when calling | |
| 266 /// [`get_authtok`](Self::get_authtok), but can be set explicitly. | |
| 267 /// | |
| 268 /// Like `get_authtok`, this should only ever be called | |
| 269 /// by *authentication* and *password-change* PAM modules. | |
| 296 get = authtok_item, | 270 get = authtok_item, |
| 297 item = "PAM_AUTHTOK", | 271 item = "PAM_AUTHTOK", |
| 298 see = Self::get_authtok, | 272 see = Self::get_authtok |
| 299 "Gets the user's authentication token (e.g., password)." | 273 ); |
| 300 "" | 274 |
| 301 "This is normally set automatically by PAM when calling " | 275 trait_item!( |
| 302 "[`get_authtok`](Self::get_authtok), but can be set explicitly." | 276 /// Gets the user's old authentication token when changing passwords. |
| 303 "" | 277 /// |
| 304 "Like `get_authtok`, this should only ever be called " | 278 /// This should only ever be called by *password-change* PAM modules. |
| 305 "by *authentication* and *password-change* PAM modules." | |
| 306 ); | |
| 307 | |
| 308 trait_item!( | |
| 309 get = old_authtok_item, | 279 get = old_authtok_item, |
| 310 item = "PAM_OLDAUTHTOK", | 280 item = "PAM_OLDAUTHTOK", |
| 311 see = PamShared::set_old_authtok_item, | 281 see = PamShared::set_old_authtok_item |
| 312 "Gets the user's old authentication token when changing passwords." | |
| 313 "" | |
| 314 "This should only ever be called by *password-change* PAM modules." | |
| 315 ); | 282 ); |
| 316 | 283 |
| 317 /* | 284 /* |
| 318 TODO: Re-enable this at some point. | 285 TODO: Re-enable this at some point. |
| 319 /// Gets some pointer, identified by `key`, that has been set previously | 286 /// Gets some pointer, identified by `key`, that has been set previously |
