|
| 1 | +//! Manipulate the contents of the shadow password file, `/etc/shadow`. |
| 2 | +use std::ffi::CStr; |
| 3 | +use std::ffi::CString; |
| 4 | + |
| 5 | +/// Represents an entry in `/etc/shadow`. |
| 6 | +// Documentation is based on the `shadow(5)` and `shadow(3)` man pages. |
| 7 | +#[derive(Debug, Clone, Eq, PartialEq, Hash)] |
| 8 | +pub struct Shadow { |
| 9 | + /// User name |
| 10 | + pub name: String, |
| 11 | + /// Encrypted password |
| 12 | + /// |
| 13 | + /// Refer to crypt(3) for details on how this string is interpreted. |
| 14 | + /// If the password field contains some string that is not a valid |
| 15 | + /// result of crypt(3), for instance ! or *, the user will not be able to |
| 16 | + /// use a unix password to log in (but the user may log in the system by |
| 17 | + /// other means). |
| 18 | + /// |
| 19 | + /// This field may be empty, in which case no passwords are required to |
| 20 | + /// authenticate as the specified login name. However, some applications |
| 21 | + /// which read the /etc/shadow file may decide not to permit any access at |
| 22 | + /// all if the password field is empty. |
| 23 | + /// |
| 24 | + /// A password field which starts with a exclamation mark means that the |
| 25 | + /// password is locked. |
| 26 | + pub password: CString, |
| 27 | + /// Date of the last password change |
| 28 | + /// |
| 29 | + /// It is expressed as the number of days since Jan 1, 1970. The value 0 |
| 30 | + /// has a special meaning, which is that the user should change their |
| 31 | + /// password the next time they will log in the system |
| 32 | + pub last_change: i64, |
| 33 | + /// Minimum password age |
| 34 | + /// |
| 35 | + /// The minimum password age is the number of days the user will have to |
| 36 | + /// wait before she will be allowed to change their password again. |
| 37 | + /// An empty field and value 0 mean that there are no minimum password age. |
| 38 | + pub min: i64, |
| 39 | + /// Maximum password age |
| 40 | + /// |
| 41 | + /// The maximum password age is the number of days after which the user |
| 42 | + /// will have to change their password. |
| 43 | + /// |
| 44 | + /// After this number of days is elapsed, the password may still be valid. |
| 45 | + /// The user should be asked to change their password the next time they |
| 46 | + /// will log in. |
| 47 | + /// |
| 48 | + /// If the maximum password age is lower than the minimum password age, the |
| 49 | + /// user cannot change their password. |
| 50 | + pub max: i64, |
| 51 | + /// Password warning period |
| 52 | + /// |
| 53 | + /// The number of days before a password is going to expire (see the |
| 54 | + /// maximum password age above) during which the user should be warned. |
| 55 | + /// |
| 56 | + /// An empty field and value 0 mean that there are no password warning |
| 57 | + /// period. |
| 58 | + pub warn: i64, |
| 59 | + /// Password inactivity period |
| 60 | + /// |
| 61 | + /// The number of days after a password has expired (see the maximum |
| 62 | + /// password age above) during which the password should still be accepted |
| 63 | + /// (and the user should update their password during the next login). |
| 64 | + /// |
| 65 | + /// After expiration of the password and this expiration period is elapsed, |
| 66 | + /// no login is possible using the current user's password. The user should |
| 67 | + /// contact their system administrator. |
| 68 | + pub inactive: i64, |
| 69 | + /// Account expiration date |
| 70 | + /// |
| 71 | + /// The date of expiration of the account, expressed as the number of days |
| 72 | + /// since Jan 1, 1970. |
| 73 | + /// |
| 74 | + /// Note that an account expiration differs from a password expiration. In |
| 75 | + /// case of an account expiration, the user shall not be allowed to login. |
| 76 | + /// In case of a password expiration, the user is not allowed to login using |
| 77 | + /// their password. |
| 78 | + /// |
| 79 | + /// The value 0 should not be used as it is interpreted as either an account |
| 80 | + /// with no expiration, or as an expiration on Jan 1, 1970. |
| 81 | + pub expire: i64, |
| 82 | +} |
| 83 | + |
| 84 | +impl From<&libc::spwd> for Shadow { |
| 85 | + fn from(spwd: &libc::spwd) -> Shadow { |
| 86 | + unsafe { |
| 87 | + Shadow { |
| 88 | + name: CStr::from_ptr((*spwd).sp_namp) |
| 89 | + .to_string_lossy() |
| 90 | + .into_owned(), |
| 91 | + password: CString::new(CStr::from_ptr((*spwd).sp_pwdp).to_bytes()).unwrap(), |
| 92 | + last_change: (*spwd).sp_lstchg, |
| 93 | + min: (*spwd).sp_min, |
| 94 | + max: (*spwd).sp_max, |
| 95 | + warn: (*spwd).sp_warn, |
| 96 | + inactive: (*spwd).sp_inact, |
| 97 | + expire: (*spwd).sp_expire, |
| 98 | + } |
| 99 | + } |
| 100 | + } |
| 101 | +} |
| 102 | + |
| 103 | +impl Shadow { |
| 104 | + /// Gets a [`Shadow`] entry for the given username, or returns [`None`]. |
| 105 | + pub fn from_name(user: &str) -> Option<Shadow> { |
| 106 | + let c_user = CString::new(user).unwrap(); |
| 107 | + |
| 108 | + let spwd = unsafe { libc::getspnam(c_user.as_ptr()) }; |
| 109 | + |
| 110 | + if spwd.is_null() { |
| 111 | + None |
| 112 | + } else { |
| 113 | + Some(unsafe { Shadow::from(&*spwd) }) |
| 114 | + } |
| 115 | + } |
| 116 | + |
| 117 | + /// Returns iterator over all entries in `shadow` file |
| 118 | + pub fn iter_all() -> ShadowIterator { |
| 119 | + ShadowIterator::default() |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +/// Iterator over `Shadow` entries |
| 124 | +/// |
| 125 | +/// # Examples |
| 126 | +/// ``` |
| 127 | +/// # use nix::shadow::ShadowIterator; |
| 128 | +/// # use nix::shadow::Shadow; |
| 129 | +/// let shadows = ShadowIterator::default().collect::<Vec<Shadow>>(); |
| 130 | +/// println!("There are {} shadow entries", shadows.len()); |
| 131 | +/// ``` |
| 132 | +/// |
| 133 | +/// # Safety |
| 134 | +/// Care should be taken when this iterator is used in different threads |
| 135 | +/// without synchronization. This is because the underlying functions used |
| 136 | +/// ([`getspent()`][1], and [`endspent()`][1]) are not thread safe. |
| 137 | +/// |
| 138 | +/// [1]: http://man7.org/linux/man-pages/man3/shadow.3.html |
| 139 | +#[derive(Debug, Default)] |
| 140 | +pub struct ShadowIterator { |
| 141 | + started: bool, |
| 142 | + done: bool, |
| 143 | +} |
| 144 | + |
| 145 | +impl Iterator for ShadowIterator { |
| 146 | + type Item = Shadow; |
| 147 | + |
| 148 | + fn next(&mut self) -> Option<Shadow> { |
| 149 | + self.started = true; |
| 150 | + if !self.done { |
| 151 | + let spwd = unsafe { libc::getspent() }; |
| 152 | + if spwd.is_null() { |
| 153 | + unsafe { libc::endspent() }; |
| 154 | + self.done = true; |
| 155 | + None |
| 156 | + } else { |
| 157 | + Some(unsafe { Shadow::from(&*spwd) }) |
| 158 | + } |
| 159 | + } else { |
| 160 | + None |
| 161 | + } |
| 162 | + } |
| 163 | +} |
| 164 | + |
| 165 | +impl Drop for ShadowIterator { |
| 166 | + fn drop(&mut self) { |
| 167 | + if self.started && !self.done { |
| 168 | + unsafe { libc::endspent() }; |
| 169 | + } |
| 170 | + } |
| 171 | +} |
0 commit comments