Skip to content

Commit fa0acdc

Browse files
ArniDagurÁrni Dagur
authored andcommitted
Add shadow.h functions
1 parent d401c6f commit fa0acdc

File tree

3 files changed

+178
-0
lines changed

3 files changed

+178
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ This project adheres to [Semantic Versioning](http://semver.org/).
77
### Added
88
- Add `CLK_TCK` to `SysconfVar`
99
(#[1177](https://github.com/nix-rust/nix/pull/1177))
10+
- Added `shadow.h` functions
11+
(#[1173](https://github.com/nix-rust/nix/pull/1173))
1012
### Changed
1113
### Fixed
1214
### Removed

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ pub mod poll;
6363
#[deny(missing_docs)]
6464
pub mod pty;
6565
pub mod sched;
66+
#[cfg(target_os = "linux")]
67+
pub mod shadow;
6668
pub mod sys;
6769
// This can be implemented for other platforms as soon as libc
6870
// provides bindings for them.

src/shadow.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
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: libc::c_long,
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: libc::c_long,
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: libc::c_long,
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: libc::c_long,
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: libc::c_long,
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: libc::c_long,
82+
}
83+
84+
impl From<&libc::spwd> for Shadow {
85+
fn from(spwd: &libc::spwd) -> Shadow {
86+
Shadow {
87+
name: unsafe { CStr::from_ptr(spwd.sp_namp).to_string_lossy().into_owned() },
88+
password: unsafe { CString::new(CStr::from_ptr(spwd.sp_pwdp).to_bytes()).unwrap() },
89+
last_change: spwd.sp_lstchg,
90+
min: spwd.sp_min,
91+
max: spwd.sp_max,
92+
warn: spwd.sp_warn,
93+
inactive: spwd.sp_inact,
94+
expire: spwd.sp_expire,
95+
}
96+
}
97+
}
98+
99+
impl Shadow {
100+
/// Gets a [`Shadow`] entry for the given username, or returns [`None`].
101+
///
102+
/// # Safety
103+
/// Care should be taken when this function is used in different threads
104+
/// without synchronization. This is because the underlying function used
105+
/// ([`getspnam()`][1]) is not thread safe.
106+
///
107+
/// [1]: http://man7.org/linux/man-pages/man3/shadow.3.html
108+
pub fn from_name(user: &str) -> Option<Shadow> {
109+
let c_user = CString::new(user).unwrap();
110+
111+
let spwd = unsafe { libc::getspnam(c_user.as_ptr()) };
112+
113+
if spwd.is_null() {
114+
None
115+
} else {
116+
Some(unsafe { Shadow::from(&*spwd) })
117+
}
118+
}
119+
120+
/// Returns iterator over all entries in `shadow` file
121+
pub fn iter_all() -> ShadowIterator {
122+
ShadowIterator::default()
123+
}
124+
}
125+
126+
/// Iterator over `Shadow` entries
127+
///
128+
/// # Examples
129+
/// ```
130+
/// # use nix::shadow::ShadowIterator;
131+
/// # use nix::shadow::Shadow;
132+
/// let shadows = ShadowIterator::default().collect::<Vec<Shadow>>();
133+
/// println!("There are {} shadow entries", shadows.len());
134+
/// ```
135+
///
136+
/// # Safety
137+
/// Care should be taken when this iterator is used in different threads
138+
/// without synchronization. This is because the underlying functions used
139+
/// ([`getspent()`][1], and [`endspent()`][1]) are not thread safe.
140+
///
141+
/// [1]: http://man7.org/linux/man-pages/man3/shadow.3.html
142+
#[derive(Debug, Default)]
143+
pub struct ShadowIterator {
144+
started: bool,
145+
done: bool,
146+
}
147+
148+
impl Iterator for ShadowIterator {
149+
type Item = Shadow;
150+
151+
fn next(&mut self) -> Option<Shadow> {
152+
self.started = true;
153+
if !self.done {
154+
let spwd = unsafe { libc::getspent() };
155+
if spwd.is_null() {
156+
unsafe { libc::endspent() };
157+
self.done = true;
158+
None
159+
} else {
160+
Some(unsafe { Shadow::from(&*spwd) })
161+
}
162+
} else {
163+
None
164+
}
165+
}
166+
}
167+
168+
impl Drop for ShadowIterator {
169+
fn drop(&mut self) {
170+
if self.started && !self.done {
171+
unsafe { libc::endspent() };
172+
}
173+
}
174+
}

0 commit comments

Comments
 (0)