Open besser82 opened 3 years ago
@ldv-alt and @solardiz This should be pretty straigh forward. I'm working on a PR to add the needed bits for making password changes possible with SELinux, too.
@ikerexxe, FYI.
I wonder wouldn't we better off if the code was written as if selinux is always enabled?
In terminology of your patch, is there any drawbacks if we assumed SELINUX_ENABLED == 1
?
I wonder wouldn't we better off if the code was written as if selinux is always enabled? In terminology of your patch, is there any drawbacks if we assumed
SELINUX_ENABLED == 1
?
Well, we would lose the sanity checks on systems without SELinux. Otherwise I don't see any.
This sounds dangerous:
* progs/tcb_chkpwd.c (unix_verify_password): Do not fail during
authentication when SELinux is enabled and the UID of the user to
be authenticated differs from the UID of the actual caller.
Also, how would tcb_chkpwd
even perform authentication for another user? What privileges would it need and have? Perhaps you mean only the case of using /etc/shadow
rather than /etc/tcb
? If so, wouldn't these changes be undesirable when using /etc/tcb
?
I wonder wouldn't we better off if the code was written as if selinux is always enabled? In terminology of your patch, is there any drawbacks if we assumed
SELINUX_ENABLED == 1
?Well, we would lose the sanity checks on systems without SELinux. Otherwise I don't see any.
These sanity checks make sense even if selinux is enabled, they just have to be adjusted so that root
is not excluded.
I wonder wouldn't we better off if the code was written as if selinux is always enabled? In terminology of your patch, is there any drawbacks if we assumed
SELINUX_ENABLED == 1
?Well, we would lose the sanity checks on systems without SELinux. Otherwise I don't see any.
These sanity checks make sense even if selinux is enabled, they just have to be adjusted so that
root
is not excluded.
You mean sth like this, I guess:
diff --git a/pam_tcb/support.c b/pam_tcb/support.c
index aa07772..c3539b8 100644
--- a/pam_tcb/support.c
+++ b/pam_tcb/support.c
@@ -475,7 +475,8 @@ static int unix_verify_password_plain(pam_handle_t *pamh,
if (!salt) {
/* we're not faking, we have an existing user, so... */
uid_t uid = getuid();
- if (SELINUX_ENABLED || (uid == geteuid() && uid == pw->pw_uid && uid != 0)) {
+ if ((SELINUX_ENABLED && uid == 0) ||
+ (uid == geteuid() && uid == pw->pw_uid && uid != 0)) {
/* We are not root perhaps this is the reason? */
D(("running helper binary"));
retval = unix_run_helper_binary(user, pass);
diff --git a/progs/tcb_chkpwd.c b/progs/tcb_chkpwd.c
index 815067f..d023a5f 100644
--- a/progs/tcb_chkpwd.c
+++ b/progs/tcb_chkpwd.c
@@ -50,7 +50,9 @@ static int unix_verify_password(const char *user, const char *pass, int nullok)
stored_hash = NULL;
if (pw) {
- if (!SELINUX_ENABLED && getuid() != pw->pw_uid)
+ uid_t uid = getuid();
+ if (((SELINUX_ENABLED && uid != 0) || !SELINUX_ENABLED) &&
+ uid != pw->pw_uid)
return AUTH_FAILED;
if (!strcmp(pw->pw_passwd, "x")) {
I mean something like this (completely untested):
--- a/pam_tcb/support.c
+++ b/pam_tcb/support.c
@@ -475,7 +475,7 @@ static int unix_verify_password_plain(pam_handle_t *pamh,
if (!salt) {
/* we're not faking, we have an existing user, so... */
uid_t uid = getuid();
- if (uid == geteuid() && uid == pw->pw_uid && uid != 0) {
- /* We are not root perhaps this is the reason? */
+ if (uid == geteuid() && (uid == pw->pw_uid || uid == 0)) {
+ /* We are not privileged enough perhaps this is the reason? */
D(("running helper binary"));
retval = unix_run_helper_binary(user, pass);
--- a/progs/tcb_chkpwd.c
+++ b/progs/tcb_chkpwd.c
@@ -43,7 +43,8 @@ static int unix_verify_password(const char *user, const char *pass, int nullok)
stored_hash = NULL;
if (pw) {
- if (getuid() != pw->pw_uid)
+ uid_t uid = getuid();
+ if (uid != pw->pw_uid && uid != 0)
return AUTH_FAILED;
if (!strcmp(pw->pw_passwd, "x")) {
I mean something like this (completely untested):
--- a/pam_tcb/support.c +++ b/pam_tcb/support.c @@ -475,7 +475,7 @@ static int unix_verify_password_plain(pam_handle_t *pamh, if (!salt) { /* we're not faking, we have an existing user, so... */ uid_t uid = getuid(); - if (uid == geteuid() && uid == pw->pw_uid && uid != 0) { - /* We are not root perhaps this is the reason? */ + if (uid == geteuid() && (uid == pw->pw_uid || uid == 0)) { + /* We are not privileged enough perhaps this is the reason? */ D(("running helper binary")); retval = unix_run_helper_binary(user, pass); --- a/progs/tcb_chkpwd.c +++ b/progs/tcb_chkpwd.c @@ -43,7 +43,8 @@ static int unix_verify_password(const char *user, const char *pass, int nullok) stored_hash = NULL; if (pw) { - if (getuid() != pw->pw_uid) + uid_t uid = getuid(); + if (uid != pw->pw_uid && uid != 0) return AUTH_FAILED; if (!strcmp(pw->pw_passwd, "x")) {
That solution works, tested on Fedora 36 (Rawhide) with a little tweak to the SELinux policy (tcb_chkpwd
needs to be labeled to type chkpwd_t
).
But pam_unix_acct.c
needs some changes as well: As the account verification (expired pw, etc.) needs to have elevated privilieges (chkpwd_t
) to work SELinux, we need it be performed through a helper binary as well.
Before I start to implement that, I'd like to hear your oppinions towards it, @ldv-alt and @solardiz, as we have two options here:
tcb_chkpwd
and call it with a cli switch to perform account verification, ortcb_chkacct
to perform this step.After that is done, I will proceed with implementing the change pw step adjustments.
@besser82 Out of the two options you list, I prefer adding a command-line option to tcb_chkpwd
, unless we can identify a reason why the potential two helpers would require different least privileges on some systems.
That said, I am not yet sure the rest of this proposal is entirely right. I'll need to take a closer look.
Also, how does pam_unix*
approach (or avoid) the same problem?
@besser82 Out of the two options you list, I prefer adding a command-line option to
tcb_chkpwd
, unless we can identify a reason why the potential two helpers would require different least privileges on some systems.
From looking at the refpolicy and the fedora targeted policy chkpwd_t
is the least required privilege to read shadow files (or other files of type shadow_t
. That said, we will definitely need a second helper for changing the user password, as that requires updpwd_t
capabilities.
That said, I am not yet sure the rest of this proposal is entirely right. I'll need to take a closer look.
I will push the reworked changes as outlined by @ldv, when I'm done with the check acct changes, will make more sense to review afterwards.
Also, how does
pam_unix*
approach (or avoid) the same problem?
They use two helper binaries: unix_chkpwd
for pass verify and acct verify (cli switches are used to switch pwd/acct) and unix_updpwd
to perform changing of user pass.
@ldv-alt and @solardiz ready for being reviewed. I'll change some wording in the Changelog, to the commit message, and to the PR when I'm squashing all commits into one for merging. I've kept them seperate for now, for easier review.
I'm sorry, I won't be able to review anything till November.
@ldv-alt Do you have some time to spend on this, yet? Don't feel to be pushed, I'm just asking to be able to do some planning.
On Wed, Nov 17, 2021 at 11:48:42AM -0800, Björn Esser wrote:
@ldv-alt Do you have some time to spend on this, yet? Don't feel to be pushed, I'm just asking to be able to do some planning.
I'll look at this as soon as I get strace 5.15 released, hopefully this weekend.
NSA Security-Enhanced Linux (SELinux) is an implementation of a flexible mandatory access control architecture in the Linux operating system. Background information and technical documentation about SELinux can be found at https://github.com/SELinuxProject.
With SELinux running in enforced mode even read-access to the shadow files, which the hashed user passwords are stored in, is restricted to processes that have at least been granted
shadow_t:file read
capabilites by the SELinux policy. For that reason the login authentication of a user must always be performed by thetcb_chkpwd
helper binary.SELINUX_ENABLED
.The needed changes have already been applied to the SELinux reference-policy in https://github.com/SELinuxProject/refpolicy/commit/bc88a1ca4b37df37c3654fc9e5368d7d96b11548.